Haskell
https://scienceblogs.com/taxonomy/term/27260/feed
enZippers: Making Functional "Updates" Efficient
https://scienceblogs.com/goodmath/2010/01/13/zippers-making-functional-upda
<span>Zippers: Making Functional "Updates" Efficient</span>
<div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><p><img src="http://upload.wikimedia.org/wikipedia/commons/f/f0/Zipper_animated.gif" class="inset right" /></p>
<p> In the Haskell stuff, I was planning on moving on to some monad-related<br />
stuff. But I had a reader write in, and ask me to write another<br />
post on data structures, focusing on a structured called a<br /><em>zipper</em>.</p>
<p> A zipper is a remarkably clever idea. It's not really a single data<br />
structure, but rather a way of building data structures in functional<br />
languages. The first mention of the structure seems to be <a href="http://www.st.cs.uni-saarland.de/edu/seminare/2005/advanced-fp/docs/huet-zipper.pdf">a paper<br />
by Gerard Huet in 1997</a>, but as he says in the paper, it's likely that this was<br />
used before his paper in functional code --- but no one thought to formalize it<br />
and write it up. <em>(In the original version of this post, I said the name of the guy who first wrote about zippers was "Carl Huet". I have absolutely no idea where that came from - I literally had his paper <b>on my lap</b> as I wrote this post, and I still managed to screwed up his name. My apologies!)</em></p>
<p> It also happens that zippers are one of the rare cases of data structures<br />
where I think it's <em>not</em> necessarily clearer to show code. The concept of<br />
a zipper is very simple and elegant - but when you see a zippered tree<br />
written out as a sequence of type constructors, it's confusing, rather<br />
than clarifying.</p>
<!--more--><p> The basic idea of a zipper is to give you a way of efficiently working with data<br />
structures in a functional language. There are a lot of cases where in an imperative<br />
language, there's some basic operation which is cheap and simple in the imperative<br />
language, because it's performed by an in-place update. But in a functional language,<br />
you can't update a field of a data structure: instead, you have to create a new copy of the structure with the altered<br />
field. </p>
<p> For example, consider the list <code>[a b c d e f g]</code>. Implemented<br />
as a cons-list, it's a list of 7 cons-cells. Suppose you wanted<br />
to replace "e" with "q". In an imperative language, that's no problem: just<br />
do a <ic>set-car!</ic> of the 5th cell. In a functional language, you would<br />
need to create a new list with "q" instead of<br />
"e". You could re-use the common tail <code>[f g]</code>, but you would need<br />
to re-create the other 5 cells: you'd need to create a new cell to<br />
attach "q" to <code>[f g]</code>. Then you'd need to create a new<br />
cell to connect "d" to <code>[q f g]</code>. And so on.</p>
<p> That makes the functional program much slower than the imperative one.<br />
If you've got a data structure that conceptually changes over time, and you're going to make lots of changes,<br />
the cost of doing it functionally can become very high, because of all of the copying<br />
you do instead of mutating a data structure.</p>
<p> In general, it's very hard to get around that. You can't update in place<br />
in a functional language (at least, not without some serious cleverness, either<br />
in your code (like monads), you language (like linear types), or your compiler).<br />
But for many applications, there's some notion<br />
of a <em>focus point</em> - that is, a particular key point where changes<br />
happen --- and you can build structures where updates <em>around the focus</em><br />
can be performed efficiently.</p>
<p> For example, if you're building a text editor, you've got the point<br />
where the cursor is sitting - and the changes all happen around the cursor.<br />
The user might type some characters, or delete some characters - but it always<br />
happens around the cursor.</p>
<p> What a zipper does is take a data structure, and unfold it around a focal<br />
point. Then you can make changes at the focal point very quickly - about as<br />
quickly as an in-place update in an imperative language.</p>
<p> The idea of it is a lot like a gap-buffer. Right now, I'm actually working<br />
on a text-editor. I'm writing it using a gap-buffer. Conceptually, an<br />
edit-buffer is one continuous sequence of characters. But if you represent it<br />
as a continuous sequence of characters, every insert is extremely expensive.<br />
So what you do is split it into two sub-sequences: one consisting of the<br />
characters <em>before</em> the cursor point, and one consisting of the<br />
characters <em>after</em> the cursor point. With that representation,<br />
inserting a character at the cursor point is O(1). Moving by one character is<br />
also O(1). Moving by N characters is O(N). With various improvements, you can<br />
do much better than that - but the key bit is that split between before the<br />
focus point and after it.</p>
<p> A zipper is a tree or graph-based version of a similar idea. For this<br />
discussion, I'll describe it in terms of trees; the graph version is more complicated,<br />
but you should be able to get the idea from seeing how it works on trees. The<br />
idea is that you take the tree structure, and you split it around a focus. You're focused on some node in the<br />
tree. You keep track of a set of nodes that come <em>before</em> you, and a<br />
set of nodes that come <em>after</em> you - those are basically like the<br />
pre-gap and post-gap regions of a gap buffer. But because you're working in a<br />
tree, you need a bit more information: you need to know the <em>path</em> from<br />
the root of the tree down to the current node. </p>
<p> It's called a zipper because what you do to create this pre-focus, path,<br />
and post-focus bits of the structure is <em>unzip</em> the tree. For example,<br />
look at the tree below. It's a representation of a string of text represented<br />
by a tree. In this particular tree, all of the data is stored in the leaves.<br />
The internal nodes contain metadata, which I haven't shown in the diagram.</p>
<div style="align: right;"><img src="http://scienceblogs.com/goodmath/wp-content/blogs.dir/476/files/2012/04/i-769e4ed7fed63a6d47a10f9112e12f1b-string-tree.png" alt="i-769e4ed7fed63a6d47a10f9112e12f1b-string-tree.png" /></div>
<p> Now, suppose I want to put the focus on "mno". To do that, I climb down<br />
the tree, <em>unzipping</em> as I go. I start at the root, node N1. Then I go<br />
right. So I put N1 and its left subtree into the left-context of my<br />
zipper-tree, and add "Right at N1" to the path. That puts the focus at N3. To<br />
get to "mno" from N3, I need to go left. So I put N3 and its right child into<br />
the right context, and add "Left at N3" to the path. Now the focus is at N4.<br />
To get to "mno", I need to go right: so I put N4 and its left child into the<br />
left context, and add "Right at N4" to the path. Now I've got the focus set<br />
where I want it at "mno"; and I've got right and left contexts.</p>
<div style="align: right;"><img src="http://scienceblogs.com/goodmath/wp-content/blogs.dir/476/files/2012/04/i-3f0252ef943a425254d267d10993ef22-string-tree-zippered.png" alt="i-3f0252ef943a425254d267d10993ef22-string-tree-zippered.png" /></div>
<p> With the zipper, you can make all sorts of changes very easily at the<br />
focus. Suppose I want to change the focus node, by inserting some text. I can<br />
do that functionally, without actually changing anything, by creating a new<br />
zipper tree which is identical to the old one, but which changes the value of<br />
the focus node - that is, if I were to add "123" right after "mno", I could do<br />
it by creating a new focus node "mno123", with the same path, left, and right<br />
contexts. It takes minimal extra memory to create the copy, because I can<br />
re-use the path and the contexts. </p>
<p> I could also add new children nodes. Suppose that instead of adding<br />
"123" to the focus, I want to keep each leaf containing three characters.<br />
could replace the focus with a new node, N5, which had children "mno"<br />
and "123". I could re-use the "mno" node, and the path, left, and right<br />
contexts.</p>
<div style="align: right;"><img src="http://scienceblogs.com/goodmath/wp-content/blogs.dir/476/files/2012/04/i-27e8c483c718300b986591cca3fcca8b-string-tree-zippered-and-edited.png" alt="i-27e8c483c718300b986591cca3fcca8b-string-tree-zippered-and-edited.png" /></div>
<p> That's the beauty of the zipper: most operations can be in terms of local<br />
changes, re-using most of the structure. If we were using a standard tree,<br />
then to add a new node in the position of "mno", we would need to create<br />
copies of N4, N3, and N1; instead, we only need to create the one new<br />
node.</p>
<p> Doing other things isn't that difficult either. Suppose we wanted to move<br />
the focus to "pqr". We'd need to shift the focus from "mno" to N3, then to N3,<br />
and then to "pqr". To get from "mno" to N4, we take the last step off of the<br />
path - which says we went right at N4 - so we set the focus to N4, and<br />
re-establish "mno" as its right child. So the focus would be N4, with "jkl" as<br />
its left child, and "mno" as its right child. To get from N4 to N3, we unroll<br />
another step of the path: since we went left at N3, that means that N3 is the<br />
new focus, with N4 as its left child. Then we'd go down to the right from N3,<br />
so we'd add "right at N3" to the path, and "pqr" would be the new focus.<br />
Moving the focus like that is a tad more difficult than just traversing<br />
non-zipper tree, but it's not significantly slower - and it makes the edits<br />
much, <em>much</em> faster.</p>
<p> So why is it harder to code? Because when we're dealing with trees, we're pretty much always dealing with balance. And balance <em>isn't</em> a local property. No matter which kind of tree you use - red/black, 2/3, AVL - you might need to climb up the tree to do the balance maintenance. That mangles the simple zipper.</p>
<p> You've got two choices. One is to re-balance<br />
the tree immediately. You can definitely do that. For<br />
example, if you think of how you do a re-balance in<br />
a red-black tree, you climb up the tree doing fixes until you've got things rebalanced. You can definitely do that - by using the zipper to move around the tree. But a big part of the point of the zipper is to keep operations local, and the re-balancing is not a<br />
local operation. Much of the time, you can do things<br />
locally, but sometimes you'll be stuck re-zipping as you move the focus up the tree fixing the balance; in<br />
the worst case, you need to re-zip the entire tree, all the way to the root.</p>
<p> The alternative is something called <em>scarring</em>. You put marks in the tree called scars that identify places where you made changes that could trigger a rebalance. (Or more generally,<br />
in places where you made an edit that could have violated some invariant of the data structure.) You don't do the fix immediately - you just mark it with<br />
the scar, and then at some point, whenever it makes sense for your application, you go back to the scars, and fix the tree. (Scaring can also have a more general meaning, which involves memorizing certain paths through<br />
the tree, so that you can make changes at the leave, then a few steps up, then back at the leaf. It's a similar concept; in both forms of scarring, you're optimizing to reduce the cost of zipping up and down the tree. )</p>
<p> Either way, it gets a bit more complicated - and when you look at the code<br />
for a zipper, the re-balancing/invariant fixing has a tendency to dominate the complexity<br />
of the code. The zipper itself is so simple and so elegant that it just disappears under<br />
the weight of tree-balancing.</p>
</div>
<span><a title="View user profile." href="https://scienceblogs.com/goodmath" lang="" about="https://scienceblogs.com/goodmath" typeof="schema:Person" property="schema:name" datatype="" xml:lang="">goodmath</a></span>
<span>Wed, 01/13/2010 - 14:23</span>
Wed, 13 Jan 2010 19:23:09 +0000goodmath92784 at https://scienceblogs.comAdvanced Haskell Data Structures: Red-Black Trees
https://scienceblogs.com/goodmath/2009/11/30/advanced-haskell-data-structur
<span>Advanced Haskell Data Structures: Red-Black Trees</span>
<div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><p> <img src="http://scienceblogs.com/goodmath/wp-content/blogs.dir/476/files/2012/04/i-8908e462401873fbddd042a25133f1fd-unbalanced-trees.jpg" alt="i-8908e462401873fbddd042a25133f1fd-unbalanced-trees.jpg" /></p><p>So, we've built up some<br />
pretty nifty binary trees - we can use the binary tree both as the basis of an<br />
implementation of a set, or as an implementation of a dictionary. But our<br />
implementation has had one major problem: it's got absolutely no way to<br />
maintain balance. What that means is that depending on the order in which<br />
things are inserted to the tree, we might have excellent performance, or we<br />
might be no better than a linear list. For example, look at these trees. As<br />
you can see, a tree with the same values can wind up quite different. In a<br />
good insert order, you can wind up with a nicely balanced tree: the minimum<br />
distance from root to leaf is 3; the maximum is 4. On the other hand, take the<br />
same values, and insert them in a different order and you get a rotten tree;<br />
the minimum distance from root to leaf is 1, and the maximum is 7. So<br />
depending on luck, you can get a tree that gives you good performance, or one<br />
that ends up giving you no better than a plain old list. Playing with a bit of<br />
randomization can often give you reasonably good performance on average - but if<br />
you're using a tree, it's probably because O(n) complexity is just too high. You<br />
want the O(lg n) complexity that you'll get from a binary tree - and<br />
not just sometimes.</p>
<p> To fix that, you need to change the structure a bit, so that as you insert<br />
things, the tree stays balanced. There are several different approaches to how<br />
you can do this. The one that we're going to look at is based on labeling nodes<br />
in ways that allow you to very easily detect when a serious imbalance is<br />
developing, and then re-arrange the tree to re-balance it. There are two<br />
major version of this, called the AVL tree, and the red-black tree. We're going<br />
to look at the red-black. Building a red-black tree is as much a<br />
lesson in data structures as it is in Haskell, but along with learning about<br />
the structure, we'll see a lot about how to write code in Haskell, and particularly<br />
about how to use pattern-matching for complex structures.</p>
<!--more--><p> We'll start with a basic definition. A red-black tree is a normal binary<br />
search tree, except that each node is assigned a <em>color</em>, which is<br />
either red or black, and there are several invariant properties that must<br />
hold about the coloring of the tree:</p>
<ol><li> The root of the tree is always black.</li>
<li> All branches of a tree end in a null which is <em>black</em>.</li>
<li> All children of red nodes are black.</li>
<li> For all nodes in the tree, all downward paths from the node to a leaf contain the<br />
same number of <em>black</em> nodes.</li>
</ol><p> If these invariants are maintained, they guarantee that tree is <em>almost</em> balanced:<br />
for the entire tree, and every subtree of it, the <em>longest</em> path from the root to<br />
a leaf is no more than twice the <em>shortest</em> path from the root to a leaf.</p>
<p> We'll start by writing the Haskell type declaration for a red/black tree. We'll just do it<br />
as a tree of ordered values to keep things simple. </p>
<pre>
>data Color = Red | Black deriving (Eq, Show)
>
>data (Ord a) => RedBlackTree a = RBTNode a Color (RedBlackTree a) (RedBlackTree a)
> | RBTEmpty deriving (Eq,Show)
</pre><p> Also, for convenience, we'll write a couple of accessor functions that we'll<br />
use later on. Something interesting to note about these accessors is that they use<br /><em>non-exhaustive patterns</em>: there are values of type <code>RedBlackTree a</code><br />
for which these functions are undefined. If you call any of these accessors on<br />
a tree whose value is <code>RBTEmpty</code>, you'll get a runtime error. </p>
<p> It is, at the very least, considered bad style to write non-exhaustive<br />
functions. It's actually a way of cheating the type system. You're claiming<br />
that you're writing a function from type T to type U, but in fact, there are<br />
values of T for which the function won't work: the real type of the function<br />
is T' -> U, where T' is a subset of T. But you can't say that in Haskell - so<br />
you're cheating. To be more concrete, you're writing functions like<br /><code>rbtLeftChild</code> which <em>claims</em> that for any red-black tree<br />
passed to it, it will return a valid red-black tree. But in fact, that's only<br />
true for the subset of red-black trees that were built with the <code>RBTNode</code><br />
constructor; for other values, the function will fail.</p>
<p> The best solution to make it exhaustive would be to use the <code>Maybe</code><br />
type to allow you to return a valid value for all trees passed as inputs. But that<br />
would make the code <em>much</em> more complex, unless we used monads - and we're<br />
not ready for monads yet.</p>
<pre>
>
>rbtLeftChild :: (Ord a) => RedBlackTree a -> RedBlackTree a
>rbtLeftChild (RBTNode _ _ l _) = l
>
>rbtRightChild :: (Ord a) => RedBlackTree a -> RedBlackTree a
>rbtRightChild (RBTNode _ _ _ r) = r
>
>rbtValue :: (Ord a) => RedBlackTree a -> a
>rbtValue (RBTNode v _ _ _) = v
>
>rbtColor :: (Ord a) => RedBlackTree a -> Color
>rbtColor (RBTNode _ c _ _) = c
>rbtColor RBTEmpty = Black
</pre><p> Inserting data into the tree is where things get interesting. It starts<br />
off the same as how you insert into a typical BST: search for the correct<br />
position, and then insert the value as a new leaf node. But in a red-black tree,<br />
the new node needs a color. New nodes are always red - so you're inserting a red<br />
node. Now you need to check to make sure that you're not violating any of the<br />
tree invariants. If you are, then you need to fix it. </p>
<p>To<br />
keep things reasonably clean and separate, we'll use the tail-calling version<br />
of tree insert, and then tail-call a rebalance function when the basic insert is<br />
complete. Rebalance will fix the balance of the tree, and do the tree<br />
re-assembly as it climbs up the tree.</p>
<pre>
>rbtInsert :: (Ord a) => RedBlackTree a -> a -> RedBlackTree a
>rbtRebalance :: (Ord a) => RedBlackTree a -> [RedBlackTree a] -> RedBlackTree a
>--rbtRebalance focus ancestors
>
>rbtInsert node v =
> rbtInsertTailCall node v []
>
>rbtInsertTailCall node@(RBTNode v color left right) newval path
> | v > newval = rbtInsertTailCall left newval (node:path)
> | otherwise = rbtInsertTailCall right newval (node:path)
>rbtInsertTailCall RBTEmpty v path =
> rbtRebalance (RBTNode v Red RBTEmpty RBTEmpty) path
</pre><p> All over the place as we rebalance the tree, we'll have places where we want to<br />
"rebuild" nodes to patch in the insertion change; as usual, we separate that into<br />
its own function.
</p>
<pre>
>-- Reconstruct takes a child node and a parent node, and creates a replacement
>-- for the parent node with the child in the appropriate position. It allows
>-- the color of the new node to be specified.
>reconstructNode node@(RBTNode v c l r) parent@(RBTNode pv pc pl pr) color =
> if (pv > v)
> then (RBTNode pv color node pr)
> else (RBTNode pv color pl node)
</pre><p> Now, we need to think about what we're going to do to keep the tree balanced as we walk<br />
back up the insertion path fixing the tree. There are two things we can do to make the<br />
tree respect the invariants: we can re-color nodes, or we can <em>pivot</em> subtrees.</p>
<p><img src="http://scienceblogs.com/goodmath/wp-content/blogs.dir/476/files/2012/04/i-f375cdfc89cfb19c174b1fa385f5cc55-unbalanced-tree.jpg" alt="i-f375cdfc89cfb19c174b1fa385f5cc55-unbalanced-tree.jpg" /></p>
<p> Pivoting a tree is an interesting operation - it's a process of swapping a node and one of<br />
its children to rotate a section of the tree. Suppose we have a binary search tree like the one<br />
in the diagram to the side. It's poorly balanced; it's got only one node to its left, but 7<br />
nodes to its right. To correct this by pivoting, what we'll do is take node 6 - currently a<br />
child of the root, and rotate the tree counterclockwise around it, so that 6 becomes the root,<br />
the old root (2) becomes the left child of 6, and the old left child of 6 (node 4) becomes the<br /><em>right</em> child of the old root. </p>
<p><img src="http://scienceblogs.com/goodmath/wp-content/blogs.dir/476/files/2012/04/i-5ebef2fdf4f03312f88fdeb436e625b4-balanced.jpg" alt="i-5ebef2fdf4f03312f88fdeb436e625b4-balanced.jpg" /></p>
<p> So after the pivot, our tree looks like this. This<br />
operation was a <em>left</em> pivot; a right pivot does the same kind of thing, but rotating<br />
the tree clockwise instead of counterclockwise. </p>
<p> So let's go ahead and write the pivot operations. We'll write two pivot<br />
functions: one for each direction. We'll pass the pivot operation<br />
a subtree whose root and child in the appropriate direction are to be rotated. In addition,<br />
we'll also add a parameter for managing the color of the new root node. In some cases,<br />
we'll want to swap the colors of the nodes being moved; in other cases, we won't. So we'll<br />
put a boolean parameter in to specify whether or not to swap the colors.</p>
<pre>
> -- pivot left tree at root; second parent indicates whether or not to swap
> -- colors of the nodes that are being moved.
>rbtPivotLeft :: (Ord a) => RedBlackTree a -> Bool -> RedBlackTree a
>rbtPivotLeft (RBTNode rootval rootcolor sib (RBTNode focval foccolor focleft focright)) swap =
> (RBTNode focval newrootcolor oldroot focright) where
> newrootcolor = if swap then rootcolor else foccolor
> oldrootcolor = if swap then foccolor else rootcolor
> oldroot = RBTNode rootval oldrootcolor sib focleft
>
>
>rbtPivotRight (RBTNode rootval rootcolor (RBTNode focval foccolor focleft focright) sib) swap =
> (RBTNode focval newrootcolor focleft oldroot) where
> newrootcolor = if swap then rootcolor else foccolor
> oldrootcolor = if swap then foccolor else rootcolor
> oldroot = RBTNode rootval oldrootcolor focright sib
>
</pre><p> So, let's try taking a look at how the pivots work. First, we need to construct<br />
some trees to rebalance. We'll just do it manually, since the insert code isn't properly<br />
finished yet.</p>
<pre>
>twentyseven = RBTNode 27 Black RBTEmpty RBTEmpty
>twentytwo = RBTNode 22 Black RBTEmpty RBTEmpty
>twentyfive = RBTNode 25 Black twentytwo twentyseven
>sixteen = RBTNode 16 Black RBTEmpty RBTEmpty
>twenty = RBTNode 20 Black sixteen twentyfive
>twelve = RBTNode 12 Black RBTEmpty RBTEmpty
>fifteen = RBTNode 15 Black twelve twenty
>two = RBTNode 2 Black RBTEmpty RBTEmpty
>seven = RBTNode 7 Black RBTEmpty RBTEmpty
>five = RBTNode 5 Black two seven
>ten = RBTNode 10 Black five fifteen
</pre><p> That produces a unbalanced binary tree that looks like this:</p>
<p><img src="http://scienceblogs.com/goodmath/wp-content/blogs.dir/476/files/2012/04/i-7e717c6351b63039f977d524f606bb53-test-balance-before.jpg" alt="i-7e717c6351b63039f977d524f606bb53-test-balance-before.jpg" /></p>
<pre>
RBTNode 10 Black
(RBTNode 5 Black -- 10left
(RBTNode 2 Black RBTEmpty RBTEmpty) -- 5 left
(RBTNode 7 Black RBTEmpty RBTEmpty)) -- 5 right
(RBTNode 15 Black -- 10 right
(RBTNode 12 Black RBTEmpty RBTEmpty) -- 15 left
(RBTNode 20 Black -- 15 right
(RBTNode 16 Black RBTEmpty RBTEmpty) -- 20 left
(RBTNode 25 Black -- 20 right
(RBTNode 22 Black RBTEmpty RBTEmpty) -- 25 left
(RBTNode 27 Black RBTEmpty RBTEmpty)))) -- 25 right
</pre><p> Let's do a quick test, and try doing a left pivot on the root.</p>
<pre>
*Main> rbtPivotLeft ten False
RBTNode 15 Black (RBTNode 10 Black (RBTNode 5 Black (RBTNode 2 Black RBTEmpty RBTEmpty) (RBTNode 7 Black RBTEmpty RBTEmpty)) (RBTNode 12 Black RBTEmpty RBTEmpty)) (RBTNode 20 Black (RBTNode 16 Black RBTEmpty RBTEmpty) (RBTNode 25 Black (RBTNode 22 Black RBTEmpty RBTEmpty) (RBTNode 27 Black RBTEmpty RBTEmpty)))
*Main>
</pre><p> Cleaned up, that looks like this: </p>
<p><img src="http://scienceblogs.com/goodmath/wp-content/blogs.dir/476/files/2012/04/i-1b31e1b5262a82c93f668be6afcbb46b-test-balance-after.jpg" alt="i-1b31e1b5262a82c93f668be6afcbb46b-test-balance-after.jpg" /></p>
<pre>
RBTNode 15 Black
(RBTNode 10 Black
(RBTNode 5 Black
(RBTNode 2 Black RBTEmpty RBTEmpty)
(RBTNode 7 Black RBTEmpty RBTEmpty))
(RBTNode 12 Black RBTEmpty RBTEmpty))
(RBTNode 20 Black
(RBTNode 16 Black RBTEmpty RBTEmpty)
(RBTNode 25 Black
(RBTNode 22 Black RBTEmpty RBTEmpty)
(RBTNode 27 Black RBTEmpty RBTEmpty)))
</pre><p> Much better - that's much closer to a balanced tree! So now that we know how to do the<br />
pivot, and we've seen that it works correctly, we can look at building the rebalance code.
</p>
<p> With pivots out of the way, we can start looking at how to decide what<br />
operations to do to rebalance the tree. When we're doing an insert, we end up<br />
inserting a red node on the bottom of the tree. It's got two children, both<br />
null, which are considered black. If the parent of our new node is black, then<br />
everything is fine; we haven't altered the number of black nodes on any path<br />
from a node to a leaf. So we're done. But if the parent is red, then we've got<br />
a red child of a red node, so we need to do some fixing.</p>
<p> Fixing an imbalance in a red-black tree can (and in fact often will)<br />
trigger a cascade of changes. But part of what makes the structure<br />
so elegant is that we only need to look at the <em>local</em> structure<br />
immediately around the new insert; and then when we've corrected that,<br />
there's only <em>one</em> place where the next problem could be. In every case<br />
where we're rebalancing, we can look at a specific problem, and fix it, and<br />
then immediately move to where the next potential problem is. To code<br />
this, we'll look at in terms of a <em>focal node</em>, which is<br />
the node causing the immediate problem we're fixing; and we'll fix the problem<br />
by looking at the local context of the focus.</p>
<p> The potential cases we can encounter are:</p>
<ol><li> The focal node is the root of the tree. In that case, we make it<br />
black. That adds one black node to every path in the tree, which<br />
leaves us with a valid tree, so we're done.</li>
<li> The focal node is red, but has a black parent. Again, that's fine. No<br />
problem.</li>
<li> The focal node is red; it's parent is also red. Then we need to look at<br />
its <em>uncle</em>; that is, the node that is the sibling of its parent. If<br />
both the new node, the parent and the uncle are all red, then we change the<br />
color of the parent and uncle to black, and the grandparent to red. After<br />
this, the grandparent becomes the focal node, and we continue to do our<br />
tree-fixing with the new focus.</li>
<li> Here's where it gets a bit messy. If the focal node and its parent are both red,<br />
but the uncle is black, then we're going to need to pivot. Getting the pivot right<br />
is tricky. There are four cases:
<ol><li> The focal node is the <em>right</em> child of its parent, and the<br />
parent is the <em>left</em> node of the grandparent, then we do a<br /><em>left</em> pivot of the focal node and its parent, and the former<br />
parent becomes the new focal node.</li>
<li> The focal node is the <em>left</em> child of its parent, and the<br />
parent is the <em>right</em> child of the grandparent, then we do a<br /><em>right</em> pivot of the focal node and its parent, and the former<br />
parent becomes the new focus.</li>
<li> The focal node is the left child of its parent, and the parent is<br />
the left child of the grandparent. Then we do a <em>right</em> pivot<br />
of the parent and the grandparent and swap the colors of the parent<br />
and grandparent. The parent becomes the focus. </li>
<li> The focal node is the right child of its parent, and the parent<br />
is the right child of the grandparent. Then we do a <em>left</em><br />
pivot of the parent and the grandparent and swap the colors of the<br />
parent and grandparent. The parent becomes the focus. </li>
</ol></li></ol><p> Ok, there's the algorithm for rebalancing. How can we code it in Haskell?<br />
We've got a list of the nodes from the insertion path, in leaf to root order.<br />
When we look at the rebalance, we can see that there are a bunch of different<br />
cases which we can separate via pattern matching:</p>
<ol><li> The focus is the root of the tree. We can select this case by<br />
using an empty list for the pattern for the ancestors parameter. Once<br />
we've gotten to the root, the tree is balanced, and the only corrective<br />
thing we may need to do is make the root black. So:
<pre>
>-- Root is focus; no matter what color it is, just make it black
>rbtRebalance (RBTNode v _ left right) [] = RBTNode v Black left right
>rbtRebalance node@(RBTNode v _ left right) (parent@(RBTNode pv pc pl pr):[])
> | pv > v = RBTNode pv pc node pr
> | otherwise = RBTNode pv pc pl node
</pre></li>
<li> Also very simple is the case where the focus is black. In that case,<br />
we don't need to do anything except patch in the insert, and continue up<br />
the tree. Again, we can select that case just by pattern matching.
<pre>
>-- black node - just patch in the change, and climb.
>
>rbtRebalance focus@(RBTNode fv Black left right) (parent@(RBTNode pv pc pl pr):ancestors)
> | pv > fv = rbtRebalance (RBTNode pv pc focus pr) ancestors
> | otherwise = rbtRebalance (RBTNode pv pc pl focus) ancestors
>
</pre></li>
<li> Next, we've got the case of a red node with a black parent. We can<br />
identify it by using "<code>RBTNode v Red left right</code>" as a pattern for the<br />
focus, and "<code>RBTNode _ Black _ _</code>" as a pattern for the parent. A<br />
red node with a black parent is OK, as long as the subtree under the red is<br />
balanced; and since we're balancing from the bottom up, we know that<br />
everything beneath this node is balanced. So:
<pre>
>rbtRebalance focus@(RBTNode fv Red left right) (parent@(RBTNode pv Black pl pr):ancestors) =
> rbtRebalance (reconstructNode focus parent Black) ancestors
</pre></li>
<li> Now we're getting to the interesting cases, which are the cases where<br />
both the node and its parent are red. We can separate two cases here: cases<br />
where we'll fix using a pivot, and cases where we'll fix using a<br />
recoloring. The way to distinguish them is by looking at the <em>uncle</em><br />
of the focus node; that is, the sibling of the nodes parent. The red-red<br />
case is complicated enough that instead of writing out huge pattern<br />
expressions, we'll simplify it by separating the function into several<br />
layers of calls, each of which does a phase of the pattern match. We want<br />
to separate out the cases where we've got a red node with a red parent and<br />
a red uncle, and the cases where we've got a red node with a red parent and<br />
a black uncle.
<p> If the focus, its parent, and its uncle are all red, then we're in a<br />
recoloring case; if the focus and its parent are red, and the uncle is black,<br />
then we're in a pivot case.</p>
<pre>
>rbtRebalance focus@(RBTNode v Red left right) (parent@(RBTNode _ Red _ _):ancestors) =
> rebalanceRedRedNode focus parent ancestors
</pre><p> To be able to recognize sub-cases when we have a red node/red parent, we need<br />
to be able to look at the path from the grandparent to the focus, and the color of the uncle. So<br />
we'll write some helper functions to get those. </p>
<pre>
>uncleColor node parent grandparent =
> if (parent == rbtLeftChild grandparent)
> then rbtColor (rbtRightChild grandparent)
> else rbtColor (rbtLeftChild grandparent)
>
>data TwoStepPath = LeftLeft | LeftRight | RightLeft | RightRight
>
>pathFromGrandparent :: (Ord a) => RedBlackTree a -> RedBlackTree a -> RedBlackTree a -> TwoStepPath
>pathFromGrandparent node@(RBTNode v _ l r) parent@(RBTNode pv _ pl pr) grand@(RBTNode gv _ gl gr)
> | pv | pv >= gv && v | pv = pv = LeftRight
> | pv >= gv && v >= pv = RightRight
</pre><p> To actually handle the red node/red parent, first we separate out the<br />
case where the red parent is the root of the tree - there are no more ancestors<br />
on the insertion path. In that case, we can just climb to root, and do the correction<br />
from there.</p>
<pre>
>
>-- node is red, parent is red, but parent is root: just go to parent(root), and fix
>-- from there.
>rebalanceRedRedNode focus@(RBTNode fv fc fl fr) parent@(RBTNode pv pc pl pr) [] =
> rbtRebalance (reconstructNode focus parent Red) []
</pre><p> Otherwise, we need to check whether the uncle was red or black. If it was black,<br />
we do a recolor correction; if it was red, we figure out what kind of pivot to do. We'll<br />
use a bunch of helper functions to make it easy.</p>
<pre>
>rebalanceRedRedNode focus parent (grand@(RBTNode gv gc gl gr):ancestors) =
> if (uncleColor focus parent grand) == Red
> then recolorAndContinue focus parent grand ancestors
> else case (pathFromGrandparent focus parent grand) of
> LeftLeft -> rbtRebalance (pivotGrandparentRight focus parent grand) ancestors
> LeftRight -> rbtRebalance (pivotParentLeft focus parent) (grand:ancestors)
> RightLeft -> rbtRebalance (pivotParentRight focus parent) (grand:ancestors)
> RightRight -> rbtRebalance (pivotGrandparentLeft focus parent grand) ancestors
</pre><p> The code above is really just using patterns for case selection. The<br />
actual work is in the helper functions that get called. They're all simple<br />
functions. First, we have some custom pivot functions - one for each direction<br />
for pivoting around a parent (the cases where the node is left of the parent,<br />
and the parent is right of the grandparent, or vise versa), and one for each<br />
direction pivoting around a grandparent (both node and parent are left<br />
children, or both are right children).</p>
<pre>
>pivotGrandparentLeft node parent@(RBTNode pv pc pl pr) grand@(RBTNode gv gc gl gr) =
> rbtPivotLeft (RBTNode gv gc gl (RBTNode pv pc pl node)) True
>
>pivotGrandparentRight node parent@(RBTNode pv pc pl pr) grand@(RBTNode gv gc gl gr) =
> rbtPivotRight (RBTNode gv gc (RBTNode pv pc node pr) gr) True
>
>pivotParentLeft node parent@(RBTNode pv pc pl pr) =
> rbtPivotLeft (RBTNode pv pc pl node) False
>
>pivotParentRight node parent@(RBTNode pv pc pl pr) =
> rbtPivotRight (RBTNode pv pc node pr) False
</pre><p> And a function to do the recoloring for when the uncle was red:</p>
<pre>
>recolorAndContinue focus@(RBTNode v c l r) parent@(RBTNode pv pc pl pr) grand@(RBTNode gv gc gl gr) ancestors =
> let path = pathFromGrandparent focus parent grand
> uncle = (case path of
> LeftLeft -> gr
> LeftRight -> gr
> RightLeft -> gl
> RightRight -> gl)
> newUncle = if (uncle == RBTEmpty)
> then RBTEmpty
> else (RBTNode (rbtValue uncle) Black (rbtLeftChild uncle) (rbtRightChild uncle))
> newparent = reconstructNode focus parent Black
> newGrandParent = (case path of
> LeftLeft -> (RBTNode gv Red newparent newUncle)
> LeftRight -> (RBTNode gv Red newparent newUncle)
> RightLeft -> (RBTNode gv Red newUncle newparent)
> RightRight -> (RBTNode gv Red newUncle newparent))
> in rbtRebalance newGrandParent ancestors
</pre></li>
</ol><p> And that, finally, is it. For the binary search tree without balancing code, the<br />
worst case is inserting a list of values in order. So let's try that, to see how<br />
well this works.</p>
<pre>
*Main> foldl (\ x y -> rbtInsert x y) RBTEmpty [1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16]
RBTNode 4 Black (RBTNode 2 Black (RBTNode 1 Black RBTEmpty RBTEmpty) (RBTNode 3 Black RBTEmpty RBTEmpty))
(RBTNode 8 Red (RBTNode 6 Black (RBTNode 5 Black RBTEmpty RBTEmpty) (RBTNode 7 Black RBTEmpty RBTEmpty))
(RBTNode 12 Black (RBTNode 10 Red (RBTNode 9 Black RBTEmpty RBTEmpty) (RBTNode 11 Black RBTEmpty RBTEmpty))
(RBTNode 14 Red (RBTNode 13 Black RBTEmpty RBTEmpty) (RBTNode 15 Black RBTEmpty (RBTNode 16 Red RBTEmpty RBTEmpty)))))
</pre><p> Since that's completely illegible, let's clean it up, and look at it in picture form:
</p>
<p><img src="http://scienceblogs.com/goodmath/wp-content/blogs.dir/476/files/2012/04/i-0bfee66c942bb60348061121b3d1038f-result-tree.jpg" alt="i-0bfee66c942bb60348061121b3d1038f-result-tree.jpg" /></p>
<p> The shortest path from root to leaf is [4,2,1]; the longest is [4,8,12,14,15,16]. Just<br />
like we promised: the longest is no more than twice the shortest. It's a pretty good<br />
search tree, and the rebalancing work isn't terribly expensive, and amortizes nicely<br />
over a long run of inserts. The insert time ends up amortizing to <b>O</b>(lg n), just like the simple<br />
binary search tree insert.</p>
</div>
<span><a title="View user profile." href="https://scienceblogs.com/goodmath" lang="" about="https://scienceblogs.com/goodmath" typeof="schema:Person" property="schema:name" datatype="" xml:lang="">goodmath</a></span>
<span>Mon, 11/30/2009 - 14:09</span>
Mon, 30 Nov 2009 19:09:59 +0000goodmath92776 at https://scienceblogs.comCreating User-Defined Types in Haskell
https://scienceblogs.com/goodmath/2009/11/23/creating-user-defined-types-in
<span>Creating User-Defined Types in Haskell</span>
<div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><ul><li><em> (This is an edited repost of one of the posts from the earlier<br />
version of my Haskell tutorial.)</em></li>
<li><em> (This file is a literate haskell script. If you save it<br />
as a file whose name ends in ".lhs", it's actually loadable and<br />
runnable in GHCI or Hugs.)</em></li>
</ul><p> Like any other modern programming language, Haskell has excellent support<br />
for building user-defined data types. In fact, even though Haskell is very<br />
much <em>not</em> object-oriented, most Haskell programs end up being centered<br />
around the design and implementation of data structures, using constructions<br />
called <em>classes</em> and <em>instances</em>.</p>
<p> In this post, we're going to start looking at how you implement data types<br />
in Haskell. What I'm going to do is start by showing you how to implement a<br />
simple binary search tree. I'll start with a very simple version, and then<br />
build up from there.</p>
<!--more--><p> There are a variety of useful primitive types in Haskell - a wide range of<br />
numeric types (arbitrary sized integers, rationals, floats, etc.), characters,<br />
and so on. There are also a lot of basic compound types provided by the<br />
standard library - the usual tuples, arrays, lists, and so on. But when you go<br />
to build your own datatypes, the main thing that you use in Haskell is<br />
something called an <em>algebraic data types</em>. An algebraic data type<br />
consists of a set of <em>variants</em>, each of which is (essentially) a tuple<br />
which has been tagged with a marker that identifies which variant it belongs<br />
to. When you define an algebraic type, Haskell creates a set of<br /><em>constructor functions</em> for the type, one for each variant.</p>
<p> As usual, talking about things like this in theory is hopelessly vague, so<br />
let's dive in and look at some code. We'll start with a very simple binary<br />
search tree containing integers. </p>
<p> The basics are pretty straightforward. A binary search tree is a data<br />
structure consisting of a collection of nodes. Every node contains a value, a<br />
left child, and a right child. The left and right children each contain<br />
another binary search tree which could contain some values, or which could be<br />
empty.</p>
<p> When you're describing a data type and you use the word "or", that's your<br />
clue that you're looking at a variant. The tree type has two cases: trees<br />
containing values, and empty trees. A declaration of that type would look<br />
like:</p>
<pre>
>data SimpleIntBST = SIBNode Integer SimpleIntBST SimpleIntBST
> | SIBEmpty
</pre><p>
</p><p>What this says is: we're defining a data type named<br /><code>SimpleIntBST</code>. There are two kinds of <code>SimpleIntBST</code><br />
values. The first kind is an <code>SIBNode</code>, which contains an integer<br />
value, a left child, and a right child. The other kind is an<br /><code>SIBEmpty</code>, which contains no values. In a language<br />
like C++ or Java, you'd have each node contain pointers to children<br />
nodes, and an empty child would be represented by a null pointer. But<br />
in Haskell, there are no pointers - and more importantly, null doesn't<br />
have a well-defined type. In Haskell, when you've got something<br />
like a null, you need to explicity define the null case using<br />
a variant, like <code>SIBEmpty</code>.</p>
<p> In the declaration, <code>SIBNode</code> and <code>SIBEmpty</code> are the<br />
names of the <em>constructor functions</em> that are used to create values. To<br />
create an <code>SIBNode</code>, you call it like a normal function with<br />
parameters of the correct type: for example, to create an <code>SIBNode</code><br />
containing the value "4" with empty left and right children, you'd write:</p>
<pre>
SIBNode 4 SIBEmpty SIBEmpty
</pre><p> Let's try that in GHCI:</p>
<pre>
*Main> SIBEmpty
<interactive>:1:0:
No instance for (Show SimpleIntBST)
arising from use of `print' at <interactive>:1:0-7
Possible fix: add an instance declaration for (Show SimpleIntBST)
In the expression: print it
In a 'do' expression: print it
</pre><p> What's wrong? The interpreter tried to print out the value returned by the<br />
expression. The interpreter outputs values using the <code>print</code><br />
function. In general in Haskell, when we have a problem like this, we look at<br />
types. What's the type of the <code>print</code> function?</p>
<pre>
*Main> :type print
print :: (Show a) => a -> IO ()
*Main>
</pre><p> The parameter to <code>print</code> must be a value of some type that is<br />
an instance of the <code>Show</code> class. But we didn't make our binary tree<br />
an instance of <code>Show</code>. Fortunately, since <code>Show</code> is such<br />
a fundamental interface, Haskell provides a nifty shorthand to make the<br />
compiler <em>generate</em> the code necessary to provide an implementation of<br />
whatever is needed for <code>Show</code>, called a <em>deriving</em> clause. Let's<br />
try again, this time with a deriving clause. We'll call the new version<br />
"IntBST" rather than "SimpleIntBST", because treating this post as a literate<br />
file, we can't re-declare "SimpleIntBST".</p>
<pre>
>data IntBST = IBNode Integer IntBST IntBST
> | IBEmpty deriving (Show)
</pre><p>Now, loading that into GHC, we'll see:</p>
<pre>
*Main> IBNode 4 IBEmpty IBEmpty
IBNode 4 IBEmpty IBEmpty
*Main>
</pre><p>Which is exactly what we want: our binary search tree is now printable.</p>
<p>A data structure like this isn't useful without some operations to work<br />
with it. So let's go ahead and do some implementing! We'll start with an<br />
insert operator:</p>
<pre>
>ibstInsert :: IntBST -> Integer -> IntBST
>ibstInsert (IBNode i left right) v
> | i > v = IBNode i (ibstInsert left v) right
> | otherwise = IBNode i left (ibstInsert right v)
>ibstInsert IBEmpty v = (IBNode v IBEmpty IBEmpty)
</pre><p> So let's look at that a bit. It starts off with an explicit type<br />
declaration. That's not strictly necessary: the compiler can infer the type of<br />
the function without any help. But it's a good practice to put it there: it's<br />
a useful bit of documentation for a human reader, and it's useful for<br />
catching problems during compilation - if you made any mistakes writing<br />
the function, the compiler might be able to catch them because the<br />
inferred type won't match the declaration.</p>
<p> After the type declaration, we get to the real definition, which is where<br />
things get interesting. I'm sure you noticed that there was something a bit<br />
strange about the type declaration for <code>IntBST</code>. In most<br />
non-functional languages, you define a new data type as some kind of a<br /><em>record</em> or <em>structure</em>, which contains a list of <em>named</em><br />
fields; when you use the structure, you access its fields by derefencing an<br />
instance of the data structure using the name of a field. But in our Haskell<br />
binary search tree, the values belonging to an instance of the data structure<br /><em>don't have names</em>. Like most functional languages, Haskell uses<br /><em>pattern matching</em> to access the pieces of a data structure. </p>
<p> A <em>pattern</em> looks like a value expression, except that it may<br />
contain some <em>unbound</em> variables. The pattern can also be followed by a<br /><em>guard expression</em> which is a boolean expression using variables bound<br />
in the pattern. If the guard is included, it's separated from the patterns by<br />
a <code>|</code> character, which is generally read as "such that". So the<br />
first declaration line of <code>ibstInsert</code> has two patterns:<br /><code>(IBNode i left right)</code>, and <code>v</code>. It's followed by a<br />
guard. So that full declaration line can be read as "ibstInsert of an IntBST<br />
matching '<code>(IBNode i left right)</code>' and a value matching<br />
'<code>v</code>' <em>such that</em> '<code>i > v</code>' returns<br />
'<code>(IBNode i left right)</code>'.</p>
<p> As a shorthand, if you have a guard expression on an indented line<br />
following a pattern with a guard, the pattern is reused with the second guard.<br />
So the second line starting with a "<code>|</code>" is used if the first guard<br />
fails. As a guard expression, "<code>otherwise</code>" can only be used as the<br /><em>last</em> guard for a pattern, and it's always true.</p>
<p> So the first case of the declaration of <code>ibstInsert</code>, in full,<br />
does the following:</p>
<ol><li> If the tree parameter is an <code>IBNode</code>, then bind "i" to<br />
the value element of the node, "left" to the left subtree, and "right"<br />
to the right subtree; and bind the variable "v" to the second parameter,<br />
which is the value to be inserted to the tree. </li>
<li> If the value in the tree node is larger than the value being<br />
inserted, then return a tree that's the same as this, except that<br />
the value is inserted into the <em>left</em> subtree.</li>
<li> Otherwise, return a tree like this one, except that the new<br />
value is inserted into the <em>right</em> subtree.</li>
</ol><p> The second case says that if the node is an empty, then return a new<br />
non-empty node with the new value as its value, and with empty left and right<br />
children.</p>
<p> If you look at the code now that you know how to read it, one thing that<br />
should hopefully be rather striking about it is just how much it looks like<br />
the explanatory pseudo-code that you'd find in an algorithms textbook. It's<br />
very straightforward code, written in a very easy to read style. (As a brief<br />
aside, this is an example of one of the things that really attracts me to<br />
Haskell programming. Code in Haskell tends to very naturally decompose into<br />
very small definitions that look and read like explanatory code from a<br />
textbook.)</p>
<p> So let's look at a quick example of using this to build a tree.</p>
<pre>
> treeone = ibstInsert (ibstInsert (ibstInsert (ibstInsert (ibstInsert IBEmpty 3) 5) 2) 17) 10
> treetwo = (ibstInsert (ibstInsert (ibstInsert (ibstInsert treeone 4) 6) 1) 9)
</pre><p>That gives us two trees, one of which contains a subset of the values in<br />
the other. We can ask ghc to show us the values of them:</p>
<pre>
*Main> treeone
IBNode 3 (IBNode 2 IBEmpty IBEmpty) (IBNode 5 IBEmpty (IBNode 17 (IBNode 10 IBEmpty IBEmpty) IBEmpty))
*Main*gt; treetwo
IBNode 3 (IBNode 2 (IBNode 1 IBEmpty IBEmpty) IBEmpty) (IBNode 5 (IBNode 4 IBEmpty IBEmpty) (IBNode 17 (IBNode 10 (IBNode 6 IBEmpty (IBNode 9 IBEmpty IBEmpty)) IBEmpty) IBEmpty))
*Main>
</pre><p> The other thing that we want to be able to do with a binary search tree is<br />
find nodes in it. Since our current BST only contains integers, we'll just<br />
write a function that returns a boolean value indicating whether or not a<br />
particular value is in the tree.</p>
<pre>
>
>ibstSearch :: IntBST -> Integer -> Bool
>ibstSearch (IBNode k left right) v
> | k == v = True
> | k > v = ibstSearch left v
> | otherwise = ibstSearch right v
>ibstSearch IBEmpty _ = False
>
</pre><p> This one should be pretty easy to read without much explanation. To be<br />
pedantic, I'll walk through it quickly. If it's a non-empty node, and the<br />
value in the node equals the value being searched for, return true. If the<br />
value in the node is bigger, then return the result of searching the left<br />
subtree; otherwise, return the result of searching the right subtree. If it's<br />
an empty node, then the value isn't there, so return false.</p>
<hr /><p> A binary search tree isn't particularly useful if it can only hold<br />
integers. So as a first step towards making it more real, we'd like to be able<br />
to store not just integers in the tree, but any value for which the necessary<br />
comparisons (<code>></code> and <code>==</code>) are defined. There's a<br />
typeclass for that, called "<code>Ord</code>", for <em>Ordered</em>. So now<br />
let's just rewrite <code>IntBST</code> as a generic BST using typeclasses:</p>
<pre>
> data (Ord a) => GenBST a = GBNode a (GenBST a) (GenBST a) > | GBEmpty
> deriving (Show)
</pre><p>The variable on the <em>left</em> of the equal in the data type definition<br />
is a type parameter. The type-class constraint preceding the name of the new<br />
datatype means that the type parameter "<code>a</code>" can <em>only</em> be<br />
instantiated by a type that is an instance of the type-class<br />
"<code>Ord</code>". The method implementations will barely change:</p>
<pre>
> gbstInsert :: (Ord a) => GenBST a -> a -> GenBST a
> gbstInsert (GBNode i left right) v
> | i > v = GBNode i (gbstInsert left v) right
> | otherwise = GBNode i left (gbstInsert right v)
> gbstInsert GBEmpty v = (GBNode v GBEmpty GBEmpty)
>
> gbstSearch :: (Ord a) => GenBST a -> a -> Bool
> gbstSearch (GBNode k left right) v
> | k == v = True
> | k > v = gbstSearch left v
> | otherwise = gbstSearch right v
> gbstSearch GBEmpty _ = False
</pre><p> With that implementation of <code>gbstInsert</code>, we can pretty much<br />
reuse the code to generate a sample instance: </p>
<pre>
> gtreeone = gbstInsert (gbstInsert
> (gbstInsert (gbstInsert (gbstInsert GBEmpty 3) 5) 2) 17) 10
> gtreetwo = (gbstInsert (gbstInsert (gbstInsert (gbstInsert gtreeone 4) 6) 1) 9)
</pre><p> Look what ghci says about those:</p>
<pre>
*Main*gt; gtreetwo
GBNode 3 (GBNode 2 (GBNode 1 GBEmpty GBEmpty) GBEmpty) (GBNode 5 (GBNode 4 GBEmpty GBEmpty) (GBNode 17 (GBNode 10 (GBNode 6 GBEmpty (GBNode 9 GBEmpty GBEmpty)) GBEmpty) GBEmpty))
*Main> :type gtreetwo
gtreetwo :: GenBST Integer
*Main> gbstSearch gtreetwo 17
True
*Main> gbstSearch gtreetwo 18
False
*Main> gbstSearch gtreetwo 1
True
*Main> gbstSearch gtreeone 1
False
*Main>
</pre><p> We can use the new generic tree in <em>exactly</em> the same way that we<br />
used the integer specific one. We don't need to declare types of expressions<br />
that use it. The compiler will happily infer the type parameter, and just make<br />
everything work. But now we're not restricted to integers:</p>
<pre>
*Main> gbstInsert (gbstInsert (gbstInsert GBEmpty "hi") "hello") "Salutations"
GBNode "hi" (GBNode "hello" (GBNode "Salutations" GBEmpty GBEmpty) GBEmpty) GBEmpty
*Main> gbstInsert (gbstInsert (gbstInsert (gbstInsert GBEmpty "hi") "hello") "S
alutations") "Greetings"
GBNode "hi" (GBNode "hello" (GBNode "Salutations" (GBNode "Greetings" GBEmpty GBEmpty) GBEmpty) GBEmpty) GBEmpty
*Main>
</pre><p> That's some of the basics. Next post, we'll build on the binary search<br />
tree, giving it some more features, and updating the insert routine so that<br />
the tree stays nicely balanced. That will lead us in to some interesting<br />
things about how you code in a language like Haskell, where you can't actually<br />
modify data structures in-place.</p>
</div>
<span><a title="View user profile." href="https://scienceblogs.com/goodmath" lang="" about="https://scienceblogs.com/goodmath" typeof="schema:Person" property="schema:name" datatype="" xml:lang="">goodmath</a></span>
<span>Mon, 11/23/2009 - 03:55</span>
Mon, 23 Nov 2009 08:55:45 +0000goodmath92774 at https://scienceblogs.comTypes in Haskell: Types are Propositions, Programs are Proofs
https://scienceblogs.com/goodmath/2009/11/17/types-in-haskell-types-are-pro
<span>Types in Haskell: Types are Propositions, Programs are Proofs</span>
<div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><p><em> (This is a revised repost of an earlier part of my Haskell tutorial.)<br /></em></p>
<p> Haskell is a strongly typed language. In fact, the type system in Haskell<br />
is both stricter and more expressive than any type system I've seen for any<br />
non-functional language. The moment we get beyond writing trivial<br />
integer-based functions, the type system inevitably becomes visible, so we<br />
need to take the time now to talk about it a little bit, in order to<br />
understand how it works.</p>
<!--more--><p> One of the most important things to recognize about Haskell's type system<br />
is that it's based on <em>type inference</em>. What that means is that in<br />
general, you <em>don't</em> need to provide type declarations. Based on how<br />
you use a value, the compiler can usually figure out what type it is. The net<br />
effect is that in many Haskell programs, you don't write <em>any</em> type<br />
declarations, but your program is still carefully type-checked. You<br /><em>can</em> declare a type for any expression you want, but the only time you<br /><em>must</em> is when something about your code is ambiguous enough that type<br />
inference can't properly select one type for an expression.</p>
<p> I'm not going to go into a lot of detail, but the basic idea behind<br />
Haskell's type system is that there's a basic propositional logic of types.<br />
Every type is represented as a statement in the type-logic. So as you'll see<br />
below, an integer is written as type <code>Integer</code>. A function<br />
from integers to integers has a type which is an implication statement:<br />
"<code>Integer -> Integer</code>", which you can read in the logic as<br />
"If the input type is Integer then the output type is Integer". The<br />
way that type inference works is that values with known types become<br />
propositions; the basic type inference process s just logical inference<br />
over the type logic.</p>
<p> The type logic is designed so that the inference process, when it analyzes<br />
an expression, it will generate the <em>most general</em> type; that is, when<br />
it's inferring a type for an expression, it will always pick the most general,<br />
inclusive type that can match the expression. And that leads to one<br />
complication for beginners: Haskell's type system is almost two different type<br />
systems - a basic type system, and a <em>meta</em>-type system. The meta-type<br />
system is based on something called <em>type classes</em>, which group related<br />
families of types together. The most general types that come up as a result of<br />
type inference are frequently based on type classes, rather than on specific<br />
concrete types.</p>
<p> A type-class in code is basically a <em>predicate</em> over a type<br />
variable. For example, the type of the parameters to the "<code>+</code>"<br />
operator must be a numeric type. But since "<code>+</code>" can be used on<br />
integers, floats, complex, rationals, and so on, the type of the<br />
"<code>+</code>" operator's parameters needs to be something that includes all<br />
of those. The way that Haskell says that is the type is "<code>Num a =><br />
a</code>" - that is, that the type a is a member of the type-class<br /><code>Num</code>. The way to understand that is "Some type 'a' such that 'a' is a<br />
numeric type.".</p>
<p> The thing to remember is that essentially, a type-class is a type for<br />
types. A <em>type</em> can be thought of as a predicate which is only true for<br />
members of that type; a <em>type-class</em> is essentially a second-order<br />
predicate over <em>types</em>, which is only true for types that are members<br />
of the type-class. What type-classes do is allow you to define a <em>general<br />
concept</em> for grouping together a family of conceptually related types.<br />
Then you can write functions whose parameter or return types are formed using<br />
a type-class; the type class defines a <em>predicate</em> which describes the<br />
properties of types that could be used in the function. </p>
<p> So if we write a function whose parameters need to be numbers, but don't<br />
need to be a <em>specific kind</em> of number, we would write it to use<br />
a type-class that described the basic properties of numbers. Then you'd be<br />
able to use <em>any</em> type that satisfied the type-predicate defined<br />
by the type-class "Num".</p>
<p> If you write a function that uses numeric operations, but you don't write<br />
a type declaration, then Haskell's type-inference system will infer types for<br />
it based on the "<code>Num</code>" type-class: "<code>Num</code>" is the most<br />
general type-class of numbers; the more things we actually <em>do</em> with<br />
numbers, the more constrained the type becomes. For example, if we use the "/"<br />
operator, instead of inferring that the type of parameter must be an instance<br />
of the "Fractional" type-class.</p>
<p> With that little diversion out of the way, we can get back to talking<br />
about how we'll use types in Haskell. Types start at the bottom with a bundle<br />
of <em>primitive</em> atomic types which are built in to the language:<br /><code>Integer</code>, <code>Char</code>, <code>String</code>,<br /><code>Boolean</code>, and quite a few more. Using those types, we can<br /><em>construct</em> more interesting types. For now, the most important<br />
constructed type is a <em>function type</em>. In Haskell, functions are just<br />
values like anything else, and so they need types. The basic form of a simple<br />
single-parameter function is "<code>a -> b</code>", where "<code>a</code>" is<br />
the type of the parameter, and "<code>b</code>" is the type of the value<br />
returned by the function.</p>
<p> So now, let's go back and look at our factorial function. What's the type<br />
of our basic "<code>fact</code>" function? According to Hugs, it's<br />
"<code>Num a => a -> a</code>".</p>
<p> Definitely not what you might expect. What's happening is that the system<br />
is looking at the expression, and picking the most general type. In fact, the<br />
only things that are done with the parameter are comparison, subtraction, and<br />
multiplication: so the system infers that the the parameter must be a<br /><em>number</em>, but it can't infer anything more than that. So it says that<br />
the type of the function parameter is a numeric type; that is, a member of the<br />
type-class "<code>Num</code>"; and that the return type of the function is the same as<br />
the type of the parameter. So the statement "<code>Num a => a -> a</code>" basically says<br />
that "<code>fact</code>" is a function that takes a parameter of <em>some</em> type<br />
represented by a <em>type variable</em> "<code>a</code>" and returns a value of the<br /><em>same</em> type; and it also says that the type variable "<code>a</code>" must be a<br />
member of the meta-type "<code>Num</code>", which is a type-class which includes all of<br />
the numeric types. So according to Haskell, as written the factorial function<br />
is a function which takes a parameter of <em>any</em> numeric type, and returns a<br />
value of the <em>same</em> numeric type as its parameter.</p>
<p> If we look at that type, and think about what the factorial function actually<br />
does, there's a problem. That type isn't correct, because factorial is only<br />
defined for integers, and if we pass it a non-integer value as a parameter, it<br />
will <em>never terminate</em>! But Haskell can't figure that out for itself -<br />
all it knows is that we do three things with the parameter to our function: we<br />
compare it to zero, we subtract from it, and we multiply by it. So Haskell's<br />
most general type for that is a general numeric type. So since we'd like to<br />
prevent anyone from mis-calling factorial by passing it a fraction (which will<br />
result in it never terminating), we should put in a type declaration to force<br />
it to take the more specific type "<code>Integer -> Integer</code>" - that is,<br />
a function from an integer to an integer. The way that we'd do that is to add<br />
an <em>explicit type declaration</em> before the function:</p>
<pre>
> fact :: Integer -> Integer
> fact 0 = 1
> fact n = n*fact(n-1)
</pre><p> That does it; the compiler accepts the stronger constraint provided by our<br />
type declaration.</p>
<p> So what we've seen so far is that a function type for a single parameter<br />
function is created from two other types, joined by the "<code>-></code>"<br />
type-constructor. With type-classes mixed in, that can be <em>prefixed</em> by<br />
type-class constraints, which specify the <em>type-classes</em> of any type<br />
variables in the function type.</p>
<p> Before moving on to multiple parameter functions, it's useful to introduce a<br />
little bit of syntax. Suppose we have a function like the following:</p>
<pre>
poly x y = x*x + 2*x*y + y*y
</pre><p> That definition is actually a shorthand. Haskell is a lambda calculus based<br />
language, so semantically, functions are really just lambda expressions: that<br />
definition is really just a binding from a name to a lambda expression.</p>
<p> In lambda calculus, we'd write a definition like that as:</p>
<pre>
poly ≡ λ x y . x*x + 2*x*y + y*y
</pre><p> Haskell's syntax is very close to that. The definition in Haskell syntax<br />
using a lambda expression would be:</p>
<pre>
poly = (\ x y -> x*x + 2*x*y + y*y)
</pre><p> The λ in the lambda expression is replaced by a backslash, which is the<br />
character on most keyboards that most resembles lambda; the "." becomes an<br />
arrow.</p>
<p> Now, with that out of the way, let's get back to multi-parameter functions.<br />
Suppose we take the poly function, and see what Hugs says about the type:</p>
<pre>
poly x y = x*x + 2*x*y + y*y
hugs> Main> :type poly<br />
hugs> poly :: Num a => a -> a -> a
</pre><p> This answer is very surprising to most people: it's a <em>two</em><br />
parameter function. So intuitively, there should by <em>some</em> grouping<br />
operator for the two parameters, to make the type say "a function that takes<br />
two a's, and returns an a"; something like "<code>(a,a) -> a</code>".
</p>
<p> But functions in Haskell are automatically <em>curried</em>. Currying is a<br />
term from mathematical logic; it's based on the idea that if a function is a<br />
value, then you don't <em>ever</em> need to be able to take more than one parameter.<br />
Instead of a two parameter function, you can write a one-parameter function<br />
that returns another one-parameter function. Since that sounds really<br />
confusing, let's take a moment and look again at our "poly" example:</p>
<pre>
poly x y = x*x + 2*x*y + y*y
</pre><p> Now, suppose we knew that "x" was going to be three. Then we could write a<br />
special one-parameter function:</p>
<pre>
poly3 y = 3*3 + 2*3*y + y*y
</pre><p> But we <em>don't</em> know "<code>x</code>". But what we <em>can</em> do<br />
is write a function that takes a <em>parameter</em> "<code>x</code>", and<br />
returns a function where all of the references to <code>x</code> are filled<br />
in, and given a y value, will return the value of the polynomial for x and<br />
y:</p>
<pre>
polyn x = (\y -> x*x + 2*x*y + y*y)
</pre><p> If we call "<code>polyn 3</code>", the result is exactly what we wrote for<br />
"<code>poly3</code>". If we call "<code>polyn a b</code>", it's semantically<br /><em>exactly</em> the same thing as "<code>poly a b</code>". (That doesn't mean<br />
that the compiler actually <em>implements</em> multi-parameter functions by<br />
generating single-parameter functions; it generates multi-parameters functions<br />
the way you'd expect. But everything <em>behaves</em> as if it did.) So what's<br />
the type of <code>polyn</code>? Well, it's a function that takes a parameter<br />
of type <code>a</code>; and returns a <em>function</em> of type "<code>Num a<br />
=> a -> a</code>". So, the type of <code>polyn</code> is "<code>Num a => a -> (a -><br />
a)</code>"; and since the precedence and associativity rules are set up to<br />
make currying convenient, the parents in that aren't necessary - that's the<br />
same as "<code>Num a => a -> a -> a</code>". Since "<code>poly</code>" and<br />
"<code>polyn</code>" are supposed to be semantically equivalent, that means<br />
that "<code>poly</code>"'s type must also be "<code>Num a => a -> a -><br />
a'".</code></p>
</div>
<span><a title="View user profile." href="https://scienceblogs.com/goodmath" lang="" about="https://scienceblogs.com/goodmath" typeof="schema:Person" property="schema:name" datatype="" xml:lang="">goodmath</a></span>
<span>Tue, 11/17/2009 - 03:27</span>
Tue, 17 Nov 2009 08:27:00 +0000goodmath92771 at https://scienceblogs.comWriting Basic Functions in Haskell (edited repost)
https://scienceblogs.com/goodmath/2009/11/13/writing-basic-functions-in-has
<span>Writing Basic Functions in Haskell (edited repost)</span>
<div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><p><em>(This is a heavily edited repost of the first article in my original<br />
Haskell tutorial.)</em></p>
<p><em>(I've attempted o write this as a literate haskell program. What that<br />
means is that if you just cut-and-paste the text of this post from your<br />
browser into a file whose name ends with ".lhs", you should be able to run it<br />
through a Haskell compiler: only lines that start with ">" are treated as<br />
code. The nice thing about this is that this blog post is itself a<br />
compilable, loadable Haskell source file - so I've compiled and tested<br />
all of the code in here in exactly this context.)</em></p>
<!--more--><p> Haskell is a functional programming language. At the simplest<br />
level, what that means is that you write programs by writing functions - and the<br />
functions are truly mathematical functions: they take a set of inputs, and generate<br />
a set of outputs, and the result of the function call is dependent solely<br />
on the values of the inputs. So, the best way to start out<br />
looking at Haskell is to look at how to write basic functions.</p>
<p> So let's dive right in a take a look at a very simple Haskell definition<br />
of the factorial function:</p>
<pre>
> simplest_fact n = if n == 0
> then 1
> else n * simplest_fact (n - 1)
</pre><p> This is the classic implementation of the factorial. Some things to<br />
notice:</p>
<ol><li> In Haskell, a function definition uses no keywords. Just "<code>name<br />
params = impl</code>"</li>
<li> Function application is written by putting<br />
things side by side. So to apply the factorial function to <code>x</code>, we<br />
just write <code>simplest_fact x</code>. Parens are only used for managing precedence.<br />
The parens in "<code>fsimplest_act (n - 1)</code>" aren't there because the function<br />
parameters need to be wrapped in parens, but because otherwise, the default<br />
precedence rules would parse it as "<code>(simplest_fact n) - 1</code>".</li>
<li> Haskell uses infix<br />
syntax for most mathematical operations. That doesn't mean that they're<br />
special: they're just an alternative way of writing a binary function<br />
invocation. You can define your own mathematical operators using normal<br />
funcion definitions.</li>
<li> There are no explicit grouping constructs in Haskell:<br />
no "{/}", no "begin/end". Haskell's formal syntax uses braces, but the<br />
language defines how to translate indentation changes into braces; in<br />
practice, just indenting the code takes care of grouping.
</li>
</ol><p> That implementation of factorial, while correct, is not how most Haskell<br />
programmers would normally implement it. Haskell includes a feature called<br /><em>pattern matching</em>, and most programmers would use pattern matching<br />
rather than the <code>if/then</code> statement for a case like that. Pattern matching<br />
separates things and often makes them easier to read. The basic idea of<br />
pattern matching in function definitions is that you can write what <em>look<br />
like</em> multiple versions of the function, each of which uses a different set of<br />
patterns for its parameters (we'll talk more about what patterns look like in<br />
detail in a later post); the different cases are separated not just by<br />
something like a conditional statement, but they're completely separated at<br />
the definition level. The case that matches the values at the point of call is<br />
the one that is actually invoked. So here's a more stylistically correct<br />
version of the factorial:</p>
<pre>
> pattern_fact 0 = 1
> pattern_fact n = n * pattern_fact (n - 1)
>
</pre><p> In the semantics of Haskell, those two are completely equivalent. In fact,<br />
the deep semantics of Haskell are based on pattern matching - so it's more<br />
correct to say that the if/then/else version is translated into the pattern<br />
matching version than vice-versa! At a low level, both will basically be<br />
expanded to the Haskell pattern-matching primitive called the<br />
"<code>case</code>" statement, which selects from among a list of patterns:
</p>
<pre>
> cfact n = case n of
> 0 -> 1
> _ -> n * cfact (n - 1)
</pre><p> Another way of writing the same thing is to use Haskell's list type. Lists<br />
are a very fundamental type in Haskell. They're similar to lists in Lisp,<br />
except that all of the elements of a list must have the same type. A list is<br />
written between square brackets, with the values in the list written inside,<br />
separated by commas, like:</p>
<pre>
[1, 2, 3, 4, 5]
</pre><p> As in lisp, the list is actually formed from pairs, where the first<br />
element of the pair is a value in the list, and the second value is the rest<br />
of the list. Pairs are <em>constructed</em> in Haskell using "<code>:</code>",<br />
so the list could also be written in the following ways:</p>
<pre>
1 : [2, 3, 4, 5]
1 : 2 : [3, 4, 5]
1 : 2 : 3 : 4 : 5 : []
</pre><p> If you want a list of integers in a specific range, there's a shorthand<br />
for it using "<code>..</code>". To generate the list of values from<br /><code>x</code> to <code>y</code>, you can write:</p>
<pre>
[ x .. y ]
</pre><p> Getting back to our factorial function, the factorial of a number "n" is<br />
the product of all of the integers from 1 to n. So another way of saying that<br />
is that the factorial is the result of taking the list of all integers from 1<br />
to n, and multiplying them together:</p>
<pre>
listfact n = listProduct [1 .. n]
</pre><p> But that doesn't work, because we haven't defined <code>listProduct</code><br />
yet. Fortunately, Haskell provides a ton of useful list functions. One of the<br />
useful types of list operations is <em>fold</em>, which comes in two versions:<br /><code>foldl</code> and <code>foldr</code>. What folds do is iterate over a<br />
list using some functions to combine elements. It takes a function to merge<br />
two values, and an initial value for the merge. Then it takes the first<br />
element in the list, and merges it with the initial value of the result. Then<br />
it takes the second value, and merges it with the result of the first. And so<br />
on.</p>
<p> The difference between <code>foldl</code> and <code>foldr</code><br />
is the associative order in which they do things: <code>foldl</code> starts<br />
from the left, and foldr starts from the right.</p>
<p> To illustrate, suppose we had a list <code>[1, 2, 3, 4, 5, 6]</code>. If<br />
we did <code>foldl (*) 1 [1, 2, 3, 4, 5, 6]</code>, it would evaluate as<br />
"(((((1 * 1) * 2) * 3) * 4) * 5) * 6". That is, it would start with 1, and<br />
then 2, and then 3, and so on. <code>foldr</code> would group<br />
right-associative; it would do "1*(2*(3*(4*(5*(6*1)))))". Since <code>*</code><br />
is associative, the result value doesn't matter. But it can make a significant<br />
difference in performance; the way that the Haskell ends up evaluating<br />
function calls, you can easily wind up with programs where <code>foldr</code><br />
can be much faster that <code>foldl</code>.</p>
<p> So, using fold, you can write the factorial using lists:</p>
<pre>
> listfact n = listProduct [ 1 .. n ]
> where listProduct lst = foldl (*) 1 lst
</pre><p> One thing that some Haskell programmers like to do is to write<br />
what they call <em>points-free</em> functions: that is, functions<br />
that do not need to name any of their parameters. You can do an amazing<br />
amount of stuff in points-free Haskell. Personally, I'm not a fan of<br />
points-free programming: I find it much harder to read. But some people<br />
love it.</p>
<p> To go points-free, you define a function in terms<br />
of series of compositions of other functions. The elements of<br />
those compositions take care of pushing the values around to<br />
get them to the right place. For factorial, it's pretty easy. There's<br />
a function called "<code>enumFromTo</code>", which takes two<br />
parameters, m and n, and generates a list of the values from m to n. Through<br />
a haskell feature called currying, if you take a two-parameter<br />
function and only give it one parameter, you get back a one-parameter<br />
function. So:</p>
<pre>
> enumFromOne = enumFromTo 1
</pre><p> is exactly the same as:</p>
<pre>
enumFromOne n = enumFromTo 1 n
</pre><p> There's a list function, "<code>product</code>" which takes the product of<br />
all of the elements of a list - it's basically a shorthand for "<code>zipwith<br />
(*) 1</code>". Finally, if you've got two functions f and g, you can<br />
write the composition of the two functions as <code>g . f</code>. So,<br />
to write a points-free factorial function, you could just write:</p>
<pre>
> pointsfree_fact = product . (enumFromTo 1)
</pre><p> I need to explain one more list function before moving on. There's a<br />
function called "<code>zipWith</code>>" for performing an operation pairwise<br />
on two lists. For example, given the lists "<code>[1,2,3,4,5]</code>" and<br />
"<code>[2,4,6,8,10]</code>", "<code>zipWith (+) [1,2,3,4,5]<br />
[2,4,6,8,10]</code>" would result in "<code>[3,6,9,12,15]</code>".</p>
<p> Now we're going to jump into something that's going to seem really<br />
strange. One of the fundamental properties of how Haskell runs a program is<br />
that Haskell is a <em>lazy</em> language. What that means is that no<br />
expression is actually evaluated until its value is <em>needed</em>. So you<br />
can do things like create <em>infinite</em> lists - since no part of the list is<br />
computed until it's needed, you can create something that defines an infinite<br />
list - but since you'll never access more than a finite number of elements of<br />
it, it's no problem. Using that, we can do some really clever things, like<br />
define the complete series of fibonnaci numbers:</p>
<pre>
> fiblist = 0 : 1 : (zipWith (+) fiblist (tail fiblist))
</pre><p>This looks very strange if you're not used to it. But if we tease it apart a<br />
bit, it's really pretty simple:</p>
<ol><li> The first element of the fibonacci series is "0".</li>
<li> The second element of the fibonacci series is "1".</li>
<li> For the rest of the series, take the full fibonacci list, and<br />
line up the two copies of it, offset by one (the full list, and the<br />
list without the first element), and add the values pairwise. </li>
</ol><p> That third step is the tricky one. It relies on the laziness of Haskell:<br />
Haskell won't compute the <em>nth</em> element of the list until you<br /><em>explicitly</em> reference it; until then, it just keeps around the<br /><em>unevaluated</em> code for computing the part of the list you haven't<br />
looked at. So when you try to look at the <em>n</em>th value, it will compute<br />
the list <em>only up to</em> the <em>n</em>th value. So the actual computed<br />
part of the list is always finite - but you can act as if it wasn't. You can<br />
treat that list <em>as if</em> it really were infinite - and retrieve any<br />
value from it that you want. Once it's been referenced, then the list up to<br />
where you looked is concrete - the computations <em>won't</em> be repeated.<br />
But the last tail of the list will always be an unevaluated expression that<br />
generates the <em>next</em> pair of the list - and <em>that</em> pair will<br />
always be the next element of the list, and an evaluated expression for the<br />
pair after it.</p>
<p> Just to make sure that the way that "<code>zipWith</code>" is working in<br />
"<code>fiblist</code>" is clear, let's look at a prefix of the parameters to<br /><code>zipWith</code>, and the result. (Remember that those three are all actually the<br />
same list! The diagonals from bottom left moving up and right are the same<br />
list elements.)</p>
<pre>
fiblist = [0 1 1 2 3 5 8 ...]
tail fiblist = [1 1 2 3 5 8 ... ]
zipWith (+) = [1 2 3 5 8 13 ... ]
</pre><p>Given that list, we can find the <em>n</em>th element of the list very<br />
easily; the <em>n</em>th element of a list <em>l</em> can be retrieved with<br />
"<code>l !! n</code>", so, the fibonacci function to get the <em>n</em>th<br />
fibonacci number would be:</p>
<pre>
> list_fib n = fiblist !! n
</pre><p> And using a very similar trick, we can do factorials the same way:</p>
<pre>
> factlist = 1 : (zipWith (*) [1..] factlist)
> factl n = factlist !! n
</pre><p> The nice thing about doing factorial this way is that the values of all of<br />
the factorials <em>less than</em> n are also computed and remembered, so the<br />
next time you take a factorial, you don't need to repeat those<br />
multiplications.</p>
</div>
<span><a title="View user profile." href="https://scienceblogs.com/goodmath" lang="" about="https://scienceblogs.com/goodmath" typeof="schema:Person" property="schema:name" datatype="" xml:lang="">goodmath</a></span>
<span>Fri, 11/13/2009 - 09:47</span>
<div class="field field--name-field-blog-categories field--type-entity-reference field--label-inline">
<div class="field--label">Categories</div>
<div class="field--items">
<div class="field--item"><a href="https://scienceblogs.com/channel/free-thought" hreflang="en">Free Thought</a></div>
</div>
</div>
Fri, 13 Nov 2009 14:47:35 +0000goodmath92769 at https://scienceblogs.comPhilosophizing about Programming; or "Why I'm learning to love functional programming"
https://scienceblogs.com/goodmath/2009/11/10/philosophizing-about-programmi
<span>Philosophizing about Programming; or "Why I'm learning to love functional programming"</span>
<div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><p> Way back, about three years ago, I started writing <a href="http://scienceblogs.com/goodmath/goodmath/programming/haskell/">a Haskell<br />
tutorial</a> as a series of posts on this blog. After getting to <a></a>
href="http://scienceblogs.com/goodmath/2007/01/haskell_a_first_step_into_mona_1.php">monads, I moved on to other things. But based on some recent<br />
philosophizing, I think I'm going to come back to it. I'll start by explaining<br />
why, and then over the next few days, I'll re-run revised versions of old<br />
tutorial posts, and then start new material dealing with the more advanced<br />
topics that I didn't get to before.</p>
<p> To start with, why am I coming back to Haskell? What changed since the<br />
last time I wrote about it?</p>
<!--more--><p> I last wrote about Haskell three years ago, when I was still working for<br />
IBM. In the time since then, I've been working for Google. It's been a very<br />
enlightening couple of years. Instead of working on isolated research<br />
code-bases, I'm working in a truly massive code-base. I've regularly<br />
written code that will be read by at least dozens of other engineers, and<br />
I regularly read code written by <em>hundreds</em> of other people.</p>
<p> At Google, we generally program in three languages: C++, Java, and Python.<br />
None of them are functional languages: are all state-heavy, imperative,<br />
object-oriented languages. But the more I've read and written code in this<br />
code-base, the more I've found that functional code is the best way of<br />
building large things. When I look at a piece of code, if the code is<br />
basically functional, I've found that it's <em>much</em> easier to understand,<br />
much easier to test, and much <em>less</em> likely to produce painful bugs.<br />
It's gotten to the point where when I see code that isn't functional, I cringe<br />
a little. Almost everything that I write ends up being at least mostly<br />
functional - the places where I use non-functional code, it's because the<br />
language and compiler aren't up to the task of keeping the code efficient.</p>
<p> Writing functional code in non-functional languages is, obviously,<br />
possible. I do it pretty much every day. But it's not easy. And it's far<br /><em>less</em> clear than it would be in a real proper functional language. As<br />
I said above, I sometimes need to compromise for efficiency; and sometimes,<br />
the language just isn't expressive in the right way to let me do things in the<br />
way that a functional programmer really would.</p>
<p> Back when I started the original Haskell tutorial, I was rather skeptical<br />
about Haskell. Functional languages have not, traditionally, been used for<br />
large, complex systems. There were lots of claims about functional systems,<br />
but no real strong evidence for those claims.</p>
<p> My experiences over the last few years have convinced me that the<br />
functional approach is, really, the correct one. But why Haskell? As I've<br />
mentioned before, I'm an obsessive programming language guy. I know way the<br />
hell too many programming languages. In the functional realm, I've learned not<br />
just Haskell - but the strict typed family (like SML and OCaml); the Lisp<br />
family (CommonLisp, Scheme), other lazy languages (Clean, Miranda, Hope), and<br />
hybrid functional languages (Erlang, Clojure). And in all of those languages,<br />
I haven't seen any that were both as clear as Haskell, and as good at managing<br />
complexity. I'm really convinced that for an awful lot of complex<br />
applications, Haskell is really right.</p>
<p> This is, in many ways, a direct contradiction of what I said when<br />
introducing Haskell the first time around. Back then, I said that the fact<br />
that Haskell was <em>referentially transparent</em> wasn't important. Referential<br />
transparency is another way of saying that the language is mathematically<br />
functional: that every expression in the language is a function from<br />
its inputs to its outputs, with no hidden parameters, no hidden<br />
mutable state that can change the result of a call.</p>
<p> At the time, I said that I thought that the most common argument in favor<br />
of referential transparency was silly. You see, people talk about referential<br />
transparency being good because it allows you to do formal reasoning about<br />
programs. It's close to impossible to reason about programs in languages<br />
like C++, where you've got things like mutable pointers to functions<br />
that contain implicit, persistent mutable state. But a lazy<br />
functional language like Haskell, you can reason about. At the time,<br />
my argument was that people don't really reason about real, non-trivial<br />
programs, and that real complex systems would still be impossible<br />
to reason about if they were written in Haskell.</p>
<p> I was wrong. Since then, I've done rather a lot of reasoning<br />
about programs. Sometimes that's been in the context of dealing<br />
with concurrency, when I've got a strange, intermittent bug which<br />
I can't reliably reproduce, and so formally reasoning about the possible<br />
behaviors of the system was the only way to figure out what was going<br />
on. Other times, I've been working on things that are just too expensive<br />
to debug - once they've shown that they can fail, you can't deploy<br />
test runs on a thousand machines to see if, maybe, you can reproduce the problem<br />
and generate a useful stack trace. Even if the cost of deploying a known<br />
buggy program weren't too expensive, sorting through stacks from<br />
a thousand machines to figure out what was going on isn't feasible. So<br />
I've wound up coming back to formal reasoning.</p>
<p> You <em>can</em> do formal reasoning about programs written in<br />
non-functional languages. But you've got to start by making assumptions - and<br />
if those assumptions are wrong, you end up wasting a huge amount of time. The<br />
style of the program has a huge impact on that: in general, the more<br />
functional the programming style, the easier it is to work out a valid set of<br />
assumptions to allow you to analyze the program. But no matter<br />
what, if the language itself is hostile to that kind of reasoning,<br />
you're going to have a much harder time of it than if you were using<br />
a language that was designed for reasoning.</p>
<p> Languages like Haskell, which have referential transparency, were designed<br />
for being analyzable and reasonable. What referential transparency does is buy<br />
you the ability to make very strong basic assertions about your system:<br />
assertions, not assumptions. In a functional language, you <em>know</em> that<br />
certain axioms are true. For example, you know that no one could have spawned<br />
a thread in the wrong place, because you can only create a thread in a<br />
threading monad; code that doesn't have access to that monad can't acquire<br />
locks, send messages, or spawn threads. If you use something like software<br />
transactional memory, you know that no one could have accidentally mutated<br />
something outside of a transaction - because it's impossible. </p>
<p> I've still got some qualms about Haskell. On one hand, it's a very elegant<br />
language, and the functional nature of it makes it a beautiful glue language.<br />
Some of the most beautiful, clear, elegant code I've ever seen is written in<br />
Haskell - and that's not because it was written by exceptional programmers,<br />
but because the nature of Haskell as a language can make things clearer than<br />
many other programming languages.</p>
<p> But it's not all good. Haskell has some serious problems. In particular,<br />
it's got two issues that worry me enough that I'm still a bit hesitant to<br />
recommend it for a lot of applications. Those two are what I call lazy<br />
confusion, and monad complexity.</p>
<p> By lazy confusion, I mean that it's often extremely difficult to predict<br />
what's going to happen in what order in a Haskell program. You can say what<br />
the result will be, but you can't necessarily say what order the steps will<br />
happen in. That's because Haskell uses <em>lazy evaluation</em>, which means<br />
that no computation in Haskell is <em>really</em> evaluated until its result<br />
is used. You can write Haskell programs that generate infinitely long lists -<br />
but it's not a problem, because no element of the list is ever evaluated until<br />
you try to use it, and you'll never use more that a finite number of elements.<br />
But lazy evaluation can be very confusing: even Haskell experts - even people<br />
who've implemented Haskell compilers! - sometimes have trouble predicting what<br />
code will be executed in what order. In order to figure out the computational<br />
complexity of algorithms or operations on data structures, people often<br />
wind up basically treating the program as if it were going to be<br />
evaluated eagerly - because analyzing the laziness is just too<br />
difficult. Laziness is <em>not</em> a bad thing; in fact, I'm pretty<br />
convinced that very frequently, it's a good thing, which can make<br />
code much cleaner and clearer. But the difficulty of analyzing<br />
it is a major concern. </p>
<p> Monad complexity is a very different problem. In Haskell, most code is<br />
completely stateless. It's a pure functional language, so most code can't<br />
possibly have side effects. There's no assignments, no I/O, nothing but pure<br />
functions in most Haskell code. But state is absolutely essential. To quote<br />
Simon Peyton-Jones, one of the designers of Haskell: "In the end, any program<br />
must manipulate state. A program that has no side effects whatsoever is a kind<br />
of black box. All you can tell is that the box gets hotter." The<br />
way that Haskell gets around that is with a very elegant concept called<br />
a <em>monad</em>. A monad is a construct in the program that allows you<br />
to create an element of state, and transparently pass it through a sequence<br />
of computations. This gives you functional semantics for a stateful<br />
computation, without having to write tons of code to pass the state<br />
around. So, for example, it lets you write code like:</p>
<pre>
>fancyHello :: IO ()
>fancyHello =
> do
> print "What is your name?"
> x print (concat ["Hello ", x])
</pre><p> Great, huh? But there's a problem: there is an object that conceptually<br />
contains the state being passed between the steps of the "do" construct. </p>
<p> The reason that that's a problem is that there are multiple different<br />
monads, to represent different kinds of state. There are monads for mutable<br />
arrays - so that you can write efficient matrix code. There are monads for<br />
parsing, so that you can write beautiful parsers. There are monads for IO, so<br />
that you can interact with the outside world. There are monads for interacting<br />
with external libraries written in non-functional libraries. There are monads<br />
for building graphical UIs. But each of them has a packet of state that needs<br />
to be passed between the steps. So if you want to be able to do more than one<br />
monadic thing - like, say, write a program with a GUI that can also read and<br />
write files - you need to be able to combine monads. And the more monads you<br />
need to combine, the more complicated and confusing things can get.</p>
<p> I'll come back to those two problems in more detail in both the revised<br />
tutorial posts, and the new posts that I'll be writing.</p>
</div>
<span><a title="View user profile." href="https://scienceblogs.com/goodmath" lang="" about="https://scienceblogs.com/goodmath" typeof="schema:Person" property="schema:name" datatype="" xml:lang="">goodmath</a></span>
<span>Tue, 11/10/2009 - 07:46</span>
Tue, 10 Nov 2009 12:46:03 +0000goodmath92766 at https://scienceblogs.comRopes: Twining Together Strings for Editors
https://scienceblogs.com/goodmath/2009/01/26/ropes-twining-together-strings
<span>Ropes: Twining Together Strings for Editors</span>
<div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><p> It's been a while since I've written about any data structures. But it just so happens that I'm actually really working on implementing a really interesting and broadly useful data structure now, called a <em>Rope</em>.</p>
<p> A bit of background, to lead in. I've got this love-hate relationship with<br />
some of the development tools that Rob Pike has built. (Rob is one of the Unix<br />
guys from Bell labs, and was one of the principal people involved in both the<br />
Plan9 and Inferno operating systems.) Rob has implemented some amazing<br />
development tools. The two that fascinate me were called Sam and Acme. The<br />
best and worst features of both are a sort of extreme elegant minimalism.<br />
There's no bloat in Rob's tools, no eye-candy, no redundancy. They're built to<br />
do a job, and do it well - but not to do any more than their intended job.<br />
(This can be contrasted against Emacs, which is a text editor that's grown<br />
into a virtual operating system.) The positive side of this is that they're<br />
incredibly effect, and they demonstrate just how simple a programmers text<br />
editor should be. I've never used another tool that is more effective than<br />
Acme or Sam. In all seriousness, I can do more of my work more easily in Sam<br />
than I can in Emacs (which is my everyday editor). But on the other hand, that<br />
extreme minimalist aesthetic has the effect of strictly eliminating any<br />
overlaps: there's one way to do things, and if you don't like it, tough. In<br />
the case of Acme and Sam, that meant that you used the mouse for damn-near<br />
everything. You couldn't even use the up and down arrows to move the cursor!
</p>
<p> This is a non-starter for me. As I've mentioned once or twice, I've got terrible<br />
RSI in my wrists. I can't use the mouse that much. I like to keep my hands on my<br />
keyboard. I don't mind using the mouse when it's appropriate, but moving my hand from<br />
the keyboard to the mouse <em>every time I want to move the cursor?</em>. No. No<br />
damned way. Just writing this much of this post, I would have had to go back and<br />
forth between the keyboard and mouse over 50 times. (I was counting, but gave up when<br />
I it 50.) A full day of that, and I'd be in serious pain.</p>
<p> I recently got reminded of Acme, because my new project at work involves using a<br />
programming language developed by Rob Pike. And Acme would really be incredibly<br />
useful for my new project. But I can't use it. So I decided to bite the bullet, and<br />
use my free time to put together an Acme-like tool. (Most of the pieces that you need<br />
for a prototype of a tool like that are available as open-source components, so it's<br />
just a matter of assembling them. Still a <em>very</em> non-trivial task, but a<br />
possible one.)</p>
<p> Which finally, leads us back to today's data structure. The fundamental piece of<br />
a text editor is the data structure that you use to represent the text that you're<br />
editing. For simplicity, I'm going to use Emacs terminology, and refer to the<br />
editable contents of a file as a <em>Buffer</em>.</p>
<p> How do you represent a buffer?</p>
<p> As usual with data structures, you start by asking: What do I need it to do? What performance characteristics are important?</p>
<p> In the case of a text buffer, you can get by with a fairly small set<br />
of basic operations:</p>
<ul><li> <em>Fast concatenation:</em> concatenating blocks of text needs to be<br /><em>really</em> fast.</li>
<li> <em>Fast insert:</em> given a point in a block<br />
of text, you need to be able to quickly insert text<br />
at that point.</li>
<li> <em>Fast delete:</em> given two points in a block of<br />
text, you need to be able to quickly remove the text between those points.</li>
<li> <em>Reasonably fast traversal: </em> Lots of algorithms,<br />
ranging from printing out the text to searching it are based<br />
on linear traversals of the contents. This doesn't have to be<br />
incredibly fast - it is an intrinsically linear process, and it's<br />
usually done in the context of something with a non-trivial cost<br />
(I/O, regular-expression scanning). But you can't afford to make it<br /><em>expensive</em>.</li>
<li> <em>Size:</em> you need to be able to store effectively unlimited<br />
amounts of text, without significant performance degradation in the<br />
operations described above.</li>
</ul><!--more--><p> Considering those requirements, you can eliminate a number of obvious<br />
alternatives:</p>
<ul><li> Simple character array: Inserts and deletes<br />
are very expensive for large documents.</li>
<li> Lists of lines: if you use a linked list, the memory cost<br />
for pointers becomes very expensive. If you use an array of<br />
lines, copy-insert costs of new lines becomes very expensive<br />
for large documents.</li>
<li> Gap buffers: a neat variation on character arrays; they have<br />
the same problem on large documents.</li>
</ul><p> Hybrids of the above can work nicely. Linked lists of character arrays<br />
can work very nicely. In fact, the structure that I'm going to describe<br />
is really a variation on the list of character arrays.</p>
<p> What works extremely well is to take the basic idea of a list of<br />
mini-buffers (that is, smallish character arrays), and tweaking it,<br />
so that instead of keeping a <em>list</em> of sub-buffers, you put<br />
the sub-buffers into a binary tree. The result is a structure called<br />
a <em>rope</em>. To the best of my knowledge, ropes were invented by<br />
Hans Boehm (of garbage collection fame), and <a href="http://www.cs.ubc.ca/local/reading/proceedings/spe91-95/spe/vol25/issue12/spe986.pdf">described in<br />
this paper</a>. Ropes are<br />
a simple, elegant data structure that's excellent for representing large string-like<br />
data structures. (The name is a pun: a real-world rope is made by twining together lots of smaller strings. A data-structure rope tangles together data-structure strings.)</p>
<p> I'm not going to present the full-blown version of ropes in this post; instead, I'm going to work up to that. In this post, I'm going to start by showing you a simplified version that illustrates the basic ideas. In later posts,<br />
I'll work up to full-blown ropes, including things like sub-rope sharing, copy-avoidance via copy-on-write, and so on.</p>
<div style="align: right;"><img src="http://scienceblogs.com/goodmath/wp-content/blogs.dir/476/files/2012/04/i-4a194ca1aea44289ce159e5ef83447d7-balanced-rope.png" alt="i-4a194ca1aea44289ce159e5ef83447d7-balanced-rope.png" /></div>
<p> The basic idea of a rope is amazingly simple. A rope is binary tree<br />
with strings in the leaves. An internal node in a rope tree doesn't contain any text - just pointers to its left and right children. The text value of a rope is the concatenation of its leaves, in left to right order. (This basic structure is also called a <em>concatenation tree</em>.) An example of a rope is shown to the side.</p>
<p> So - if you've got two huge strings of text that you want to concatenate, you just create a new root node, and put the two strings in as children. Presto! You've got a new rope containing the concatenation! For example, below are two rope-trees - one for the text "abcdefghijkl", and one for the text "zyxwv". Beneath them is the result of concatenating the two ropes.</p>
<div style="align: center;"><img src="http://scienceblogs.com/goodmath/wp-content/blogs.dir/476/files/2012/04/i-4be4d99016e2ea69148df16e5e745a17-concat.png" alt="i-4be4d99016e2ea69148df16e5e745a17-concat.png" /></div>
<p> Concatenation is therefore constant time, regardless of the length of the strings<br />
being concatented. - you can't ask for better than that! Traversing the text is also<br />
really easy - you just do an in-order traversal of the rope-tree. In the worst case -<br />
a degenerate tree with one character per node - that gets very expensive. But it's<br />
easy to avoid the degenerate case, as we'll see in a later post. In the typical case,<br />
where each leaf node has a reasonable amount of text, and the tree is reasonably<br />
balanced, the additional cost of traversing the tree is negligible in comparison to<br />
the cost of traversing the characters in the nodes. (Each pointer in the tree needs<br />
to be traversed once, which makes the total cost O(n) in the length of the text. But<br />
since there's typically a lot of text per leaf, that ends up meaning that the<br />
increase in traversal cost is minimal.)</p>
<p> For string-mutation operations, we can define almost anything we want in terms of<br />
two primitives: concatenate, and subrope. For example, we can define how to<br />
do insert and deletion of chunks of text from a rope as follows:</p>
<ul><li> Remove text: to remove a chunk of text that runs from point P1 to point<br />
P2 in a rope, we take the subropes (0, P1) and (P2+1,end), and then<br />
we concatenate them. The result is the rope with the chunk removed.</li>
<li> Insert text: to insert a chunk of text C into a rope at a particular<br />
point p, we take the pre-subrope from 0 to P, and the post-subrope<br />
from P+1 to the end. Then we concatenate pre-subrope, C, and<br />
post-subrope.</li>
</ul><p> So to provide those operations, we just need to figure out how to do substring in<br />
an efficient way.</p>
<p> Substring isn't trivial in ropes. But it's not awful either. The main trick is<br />
that there are a lot of cases to consider to get an implementation correct.</p>
<p> The basic idea is fairly simple. You traverse the tree, keeping track<br />
of your position in the traversal. When you get to the substring you<br />
want, you start copying - usually just copying the pointers to nodes. You<br />
keep copying until you get to the end of the desired substring. The catch,<br />
as I said above, is keeping track of all of the cases.</p>
<p> An outline of substring is:</p>
<ul><li> Inputs:
<ul><li> A rope, R.</li>
<li> The start position of the desired sub-rope, Start.</li>
<li> The length of the desired sub-rope, L.
</li></ul></li>
<li>Algorithm:
<ol><li> Let currentPosition = 0</li>
<li> Let remainingLengthToCopy = L</li>
<li> Let result = EmptyRope</li>
<li> For each node N in the tree:</li>
<li> If N is an internal node: traverse the left subtree, and then the right<br />
subtree.</li>
<li> If N is a leaf, then:
<ol><li> The start position of this node is currentPosition;<br />
the end position of this node is currentPosition plus the<br />
length of the string in this node.</li>
<li> If the start position of the desired subrope is<br />
beyond the end of this leaf, then just increment<br />
currentPosition by the size of the string in this leaf.</li>
<li> If the start position of the desired subrope is inside<br />
of this leaf, then start copying - produce a new<br />
rope node containing the substring (There are sub-cases<br />
here for "subrope also ends in this node", and "subrope"<br />
doesn't end in this node.) Increment currentPosition by<br />
the size of the string in this node, and decrement<br />
remainingLengthToCopy by the size of the copied substring.</li>
<li> If the start position of the desired subrope is before this<br />
node, and the end position of the desired subrope is after<br />
this node, then insert this node into the result rope,<br />
increment the position by the size of the string in this node,<br />
and decrement remainingLengthToCopy by the length of the string<br />
in this node.</li>
<li> If the start position of the subrope is before this node,<br />
and the end position is in the node, then copy the appropriate<br />
substring to a new leaf, and concatenate with result. Then<br />
return result.</li>
<li> If the start position of this node is after the end of the desired<br />
sub-rope, return result.</li>
</ol></li></ol></li>
</ul><p> An example implementation is below. It's a quick one that I whipped together in<br />
Haskell. (I actually wrote versions in Haskell, OCaml, Python, and Java, but in<br />
the end, I think that the Haskell is the easiest to follow.)</p>
<pre>
module Ropes where
<em>-- A rope is either an internal node with two children and no text,
-- or a leaf node with text and no children.</em>
data Rope = InternalRopeNode Rope Rope
| LeafRopeNode String
deriving (Show)
concatRope :: Rope -> Rope -> Rope
concatRope (LeafRopeNode "") r = r
concatRope r (LeafRopeNode "") = r
concatRope r1 r2 =
InternalRopeNode r1 r2
<em>-- Subrope implemented using a fairly standard translation
-- of iteration to recursion. The state of each iteration is
-- represented by a triple consisting of the current position in
-- the rope, the number of characters still to be copied, and
-- the current subrope.</em>
subRopeIter :: Rope -> Int -> Int -> (Int, Int, Rope) -> (Int, Int, Rope)
<em>-- For an internal node, call subRope of the left child on the input
-- state, and then take the state resulting from the left-child invocation,
-- and use it as the input to the right child. There is a nice Haskell
-- syntax for this, but I think the let is clearer for non-Haskell
-- people.</em>
subRopeIter (InternalRopeNode left right) start len (pos, remaining, result) =
let (leftPos, leftRemaining, leftResult) =
subRopeIter left start len (pos, remaining, result)
in subRopeIter right start len (leftPos, leftRemaining, leftResult)
<em>-- The real work happens on leaf nodes. We define it by cases:</em>
subRopeIter rope@(LeafRopeNode s) start len state@(pos, remaining, result)
<em>-- (1) node_start + len(s) < start: node content comes before the start point.
-- Just increment the position past the node and continue.</em>
| remaining == 0 = state
<em>-- (2) node_start < start and node_start + len(s) > start:
-- Copy substring, decrement remaining, increment position.
-- (2a) node_start + len(s) > pos + remaining
-- (2b) node_start + len(s) < pos + remaining</em>
| pos + length(s) < start = (pos + (length s), remaining, result)
| pos < start && pos + (length s) > start =
let startPosInNode = (start - pos)
substr = (drop startPosInNode s)
in if (length substr) < remaining
then (pos + (length s),
remaining - (length substr),
(concatRope result LeafRopeNode substr)))
else (pos + (length s),
0,
(concatRope result (LeafRopeNode (take remaining substr))))
<em>
-- (3) node_start > start and cur_remaining > 0 and cur_remaining > len(s):
-- Node lies entirely within the copy range. Copy entire node into
-- result, decrement remaining, increment position.</em>
| pos > start && length(s) <= remaining =
(pos + (length s), remaining - length(s), (concatRope result rope))
<em>-- (4) node_start > start + len (aka remaining == 0):
-- Increment pos. </em>
| pos > start && length(s) > remaining =
(pos + length(s), 0, (concatRope result (LeafRopeNode (take remaining s))))
| otherwise = state
subRope :: Rope -> Int -> Int -> Rope
subRope r start len =
let (_, _, result) = subRopeIter r start len (0, len, LeafRopeNode(""))
in result
</pre><p> For example, we can define a non-trivial rope as follows:</p>
<pre>
let r = InternalRopeNode
(InternalRopeNode
(LeafRopeNode "abc")
(InternalRopeNode
(LeafRopeNode "def")
(LeafRopeNode "ghi")))
(InternalRopeNode
(LeafRopeNode "jkl")
(LeafRopeNode "mno"))
</pre><p> That's a fairly complicated rope-tree of "abcdefghijklmno". In a real rope application, a string that short would really just be one node. But for illustration,<br />
it'll do. To get the sub-rope of length 7 starting from position 5, we'd run:<br /><code>subRope r 5 7</code>. When I do that in GHCI, I get the following:</p>
<p><code><br />
InternalRopeNode (InternalRopeNode (LeafRopeNode "f") (LeafRopeNode "ghi")) (LeafRopeNode "jkl")<br /></code></p>
<p> Or "fghijkl" - exactly right. And to do the substring operation, we needed<br />
to traverse the rope tree, copy two pointers (to the "ghi" and "jkl" nodes),<br />
create one new leaf node (for "f"), and create two new internal nodes for<br />
the concatenations.</p>
</div>
<span><a title="View user profile." href="https://scienceblogs.com/goodmath" lang="" about="https://scienceblogs.com/goodmath" typeof="schema:Person" property="schema:name" datatype="" xml:lang="">goodmath</a></span>
<span>Mon, 01/26/2009 - 16:14</span>
Mon, 26 Jan 2009 21:14:17 +0000goodmath92685 at https://scienceblogs.comMeta out the wazoo: Monads and Monoids
https://scienceblogs.com/goodmath/2008/03/11/meta-out-the-wazoo-monads-and
<span>Meta out the wazoo: Monads and Monoids</span>
<div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><p> Since I mentioned the idea of monoids as a formal models of computations, <a href="http://scienceblogs.com/goodmath/2008/02/full_circle_the_categorical_mo.php#comment-768378">John<br />
Armstrong made the natural leap ahead</a>, to the connection between monoids and monads - which are<br />
a common feature in programming language semantics, and a prominent language feature in <a></a>
href="http://scienceblogs.com/goodmath/goodmath/programming/haskell/">Haskell, one of my favorite programming languages.</p>
<!--more--><p> Monads are a category theoretic construction. If you take a monoid, and use some of the constructions we've seen in the last few posts, we can move build a meta-monoid; that is,<br />
a monoid that's built from monoid-to-monoid mappings - essentially, the category of<br />
small categories. (Small categories are categories whose collection of objects are a<br />
set, not a proper class.)</p>
<p> We're going to look at constructs built using objects in that category. But first (as usual), we need to come up with a bit of notation. Suppose we have a category, C. In the category of categories, there's an <em>identity morphism</em> (which is also a functor) from C to C. We'll<br />
call that 1<sub>C</sub>. And given any functor from T:C→C (that is, from C to itself),<br />
we'll say that <em>exponents</em> of that functor are formed by self-compositions of T: T<sup>2</sup>=TºT; T<sup>3</sup>=TºTºT, etc. Finally, given a functor T,<br />
there's a natural transformation from T to T, which we'll call 1<sub>T</sub>.</p>
<p> So, now, as I said, a monad is a construct in this category of category - that is, a particular<br />
category with some additional structure built around it. Given a category, C, a monad on C<br />
consists of three parts:</p>
<ul><li> T:C→C, a functor from C to itself.</li>
<li> A natural transformation, η:1<sub>C</sub>→T</li>
<li> A natural transformation μ:T<sup>2</sup>→T</li>
</ul><p> C, T, η, and μ must satisfy some <em>coherence conditions</em>, which<br />
basically mean that they must make the following two diagrams commute. First, we<br />
show a requirement that in terms of natural transformations, μ is commutative in<br />
how it maps T<sup>2</sup> to T:</p>
<p><img src="http://scienceblogs.com/goodmath/wp-content/blogs.dir/476/files/2012/04/i-3708df09409a7a561b25a0597ce652f3-monad-prop1.jpg" alt="i-3708df09409a7a561b25a0597ce652f3-monad-prop1.jpg" /></p>
<p> And then, a commutativity requirement on μ and η with respect to T (basically<br />
making μ and η into a meta-identity for this meta-monoid):</p>
<p><img src="http://scienceblogs.com/goodmath/wp-content/blogs.dir/476/files/2012/04/i-7446dbf8a04d1331f612c90223413813-monad-prop2.jpg" alt="i-7446dbf8a04d1331f612c90223413813-monad-prop2.jpg" /></p>
<p> μ and η basically play the role of turning C into a meta-meta-monoid. A monoid is<br />
basically a category; then we play with it, and construct the category of categories - the first<br />
meta-monoid. Now we're taking a self-functor of the meta-monoid, and and using natural<br />
transformations to build a new meta-meta-monoid around it.</p>
<p> One of the key things to notice here is that we're building a monoid whose objects are,<br />
basically, functions from monoids to monoids. We've gone meta out the wazoo - but it's given us<br />
something really interesting.</p>
<p> We start with the category. From the category, we get the functor - a structure preserving map<br />
from the category to itself. The monad focuses on the functor - the transition from C to C: using<br />
natural transformations, it defines an equivalence - not an equality, but an equivalence - between<br />
multiple applications of the functor and a single application.</p>
<p> In terms of programming languages, we can think of C as a <em>state</em>. An application<br />
of the functor T is an <em>action</em> - that is, a mapping from state to state. What the monad<br />
does is provide a structure for composing actions. We don't need to write the state - it's<br />
implicit in the definition of the functor/action. The monad says that if we have an action "X" and an action "Y", we can compose them into an action "X followed by Y". What the natural transformation says is that "X followed by Y" is an action - we can compose sequences of<br />
actions, and the result is always an action - which we can compose further, producing other<br />
compound actions. </p>
<p> So at the bottom, we have functions that are state-to-state transformers. But we don't<br />
really need to think much about the complexity of a state-to-state transition. What<br />
we can do instead is provide a collection of primitive actions - which are themselves<br />
written as state-to-state transitions - and then use those primitives to build<br />
imperative code - which remains completely functional under the covers, and yet has<br />
all of the properties that we would want from an imperative programming system -<br />
ordering, updatable state, etc.</p>
<p> Below is a really simple piece of Haskell code using the IO monad. What the monad does is play<br />
with IO states. The category is the set of IO states. Each action is a transformation from state to<br />
state. The state is <em>invisible</em> -- it's created at the beginning of the "do", and each<br />
subsequent statement is implicitly converted to a state transition function. </p>
<pre>
hello :: IO ()
hello =
do
print "What is your name?"
x <- getLine
print (concat ["Hello", x])
</pre><p> So in the code above, "<code>print "What is your name"</code>"is an action from an IO state to an IO state. It's composed with <code>x <- getLine</code> - which is, implicitly,<br />
another transition from an IO state to an IO state, which includes an implicit variable<br />
definition; and that's composed with the finat "<code>print</code>". The actions are sequenced - they occur in the correct order, and each passes its result state to next action. The monad<br />
lets us program completely in terms of the actions, without worrying about how to pass the states.</p>
</div>
<span><a title="View user profile." href="https://scienceblogs.com/goodmath" lang="" about="https://scienceblogs.com/goodmath" typeof="schema:Person" property="schema:name" datatype="" xml:lang="">goodmath</a></span>
<span>Tue, 03/11/2008 - 05:57</span>
Tue, 11 Mar 2008 09:57:26 +0000goodmath92559 at https://scienceblogs.comUsing Monads for Control: Maybe it's worth a look?
https://scienceblogs.com/goodmath/2007/02/20/using-monads-for-control-maybe-1
<span>Using Monads for Control: Maybe it's worth a look?</span>
<div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><p> So, after our last installment, describing the theory of monads, and the previous posts, which focused on representing things like state and I/O, I thought it was worth taking a moment to look at a different kind of thing that can be done with monads. We so often think of them as being state wrappers; and yet, that's only really a part of what we can get from them. Monads are ways of tying together almost <em>anything</em> that involves sequences.</p>
<!--more--><p> In previous parts of this tutorial, we've seen the <code>Maybe</code> type. It's a useful type for all sorts of things where there <em>might</em> be a value. For example, a dictionary<br />
lookup function would often be written something like:</p>
<pre>
lookup :: (Ord a) => Dictionary a b -> a -> Maybe b
</pre><p> You might expect <code>(Ord a) => Dictionary a b -> a -> b</code>, meaning a function from a dictionary and a value of type a to a value of type b. But that's not a great type definition for a lookup, because then what would the function return when the key isn't in the dictionary? <code>Maybe</code> gives us a way of handling that: if the key is included, we'll return <code>Just b</code>; otherwise, we'll return <code>Nothing</code>.</p>
<p> The problem with <code>Maybe</code> is that anytime you use it, you need to wrap<br />
things up in pattern matches to check if a <code>Nothing</code> value was returned, and to extract a value if a <code>Just</code> was returned. That gets particularly messy if you have a <em>sequence</em> of things to be looked up. For example, imagine that we're writing a program for the police. They've got a license plate number, and they'd like to get the phone number of the person who owns the car with that plate. But the data is in different places: there's a list of license plate numbers associated with owners; and then there's the telephone book which has listings of peoples names and phone numbers. So here are some trivial implementations of the basic types and lookup functions:</p>
<pre>
>data LicensePlate = Plate String deriving (Eq,Show)
>
>getPlateOwner :: [(LicensePlate,String)] -> LicensePlate -> Maybe String
>getPlateOwner ((p, n):plates) plate
> | p == plate = Just n
> | otherwise = getPlateOwner plates plate
>getPlateOwner [] _ = Nothing
>
>data PhoneNumber = Phone String String String deriving (Eq,Show)
> -- area code, prefix, number
>getPhoneNumberForName :: [(PhoneNumber,String)] -> String -> Maybe PhoneNumber
>getPhoneNumberForName ((num,name):phonebook) person
> | name==person = Just num
> | otherwise = getPhoneNumberForName phonebook person
>getPhoneNumberForName [] _ = Nothing
</pre><p> Suppose we had some databases for that:</p>
<pre>
>plateDB = [(Plate "abc", "Mark"),(Plate "h1a j2b", "Joe Random"), (Plate "u2k 5u1", "Jane Random")]
>
>phoneDB=[(Phone "321" "123" "3456", "Joe Random"), (Phone "345" "657" "3485", "Mark"), (Phone "588" "745" "1902", "Foo Bar")]
</pre><p> Now, suppose we wanted to write a lookup function from plate to phone number. The naive way would be:</p>
<pre>
>getPhoneForPlate :: [(LicensePlate,String)] -> [(PhoneNumber,String)] -> LicensePlate -> Maybe PhoneNumber
>getPhoneForPlate ldb pdb lic =
> let person = getPlateOwner ldb lic
> in case person of
> Just name -> getPhoneNumberForName pdb name
> Nothing -> Nothing
</pre><p> Now, imagine if we wanted to add another layer - we'd need to add <em>another</em> let/case inside of the "<code>Just name</code>" case. The more we use <code>Maybe</code>, the messier it gets. But <code>Maybe</code> is a monad! So we could use:</p>
<pre>
>mGetPhoneForPlate :: [(LicensePlate,String)] -> [(PhoneNumber,String)] -> LicensePlate -> Maybe PhoneNumber
>mGetPhoneForPlate ldb pdb lic =
> do
> name phone return phone
</pre><p> What happened here is that "<code>Maybe</code>" as a monad gives us a great way of chaining. Anywhere in the chain, if any step returns "Nothing", then the entire series will stop there, and return "<code>Nothing</code>". Any step where it returns "<code>Just x</code>", we can just capture the value as if there were no "<code>Just</code>". Suddenly chains of <code>Maybe</code> aren't a problem anymore.</p>
<p> An important thing to notice about this is that the monad is being used as a control structure. We've got a <em>sequence</em> of operations chained together in a monad. When we evaluate the monadic sequence, the implementation of the binding operator that combines monads actually does control flow management. Let's take a quick look at the internals of <code>Maybe</code> just to see how that's done:</p>
<pre>
instance Monad Maybe where
return = Just
fail = Nothing
Nothing >>= f = Nothing
(Just x) >>= f = f x
</pre><p> Pretty simple, right? <code>return</code> really just wraps the value in a "<code>Just</code>". If there's an error along the way, "<code>fail</code>" will make the monad evaluate to "<code>Nothing</code>". And for chaining, "<code>Nothing</code>" chained with anything else results in "<code>Nothing</code>"; <code>(Just x)</code> chained with something unwraps <code>x</code>, and passes it to the next step in the chain. The control flow is captured in the pattern match between "<code>Nothing</code>" and "<code>Just x</code>" in the instance declaration of the "<code>>>=</code>" chaining operator.</p>
<p> This basic trick, of using the chaining operator to insert control flow into monadic sequences is an incredibly useful technique, which is used by a variety of really cool monads. Some examples that we'll look at in later installments are<br />
monads for backtracking computation (like prolog); for non-deterministic computation; and for a really remarkably elegant way of building parsers.</p>
<p><em>(In fact, if you're <b>not</b> very lucky, and I have time over the next couple of days to finish it, on friday I'll post one of my own pathological programming languages, which has a parser implemented using the parsing monad. It's a language based on Post's tag systems, which form the most frustratingly simple Turing equivalent computing system that I know of.)</em></p>
</div>
<span><a title="View user profile." href="https://scienceblogs.com/goodmath" lang="" about="https://scienceblogs.com/goodmath" typeof="schema:Person" property="schema:name" datatype="" xml:lang="">goodmath</a></span>
<span>Tue, 02/20/2007 - 14:52</span>
<div class="field field--name-field-blog-categories field--type-entity-reference field--label-inline">
<div class="field--label">Categories</div>
<div class="field--items">
<div class="field--item"><a href="https://scienceblogs.com/channel/free-thought" hreflang="en">Free Thought</a></div>
</div>
</div>
Tue, 20 Feb 2007 19:52:43 +0000goodmath92271 at https://scienceblogs.comBasics: The Turing Machine (with an interpreter!)
https://scienceblogs.com/goodmath/2007/02/03/basics-the-turing-machine-with-1
<span>Basics: The Turing Machine (with an interpreter!)</span>
<div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><p> As long as I'm doing all of these basics posts, I thought it would be worth<br />
explaining just what a Turing machine is. I frequently talk about things<br />
being Turing equivalent, and about effective computing systems, and similar things, which all assume you have some clue of what a Turing machine is. And as a bonus, I'm also going to give you a nifty little piece of Haskell source code that's a very basic Turing machine interpreter. (It's for a future entry in the Haskell posts, and it's not entirely finished, but it <em>does</em> work!)</p>
<p> The Turing machine is a very simple kind of theoretical computing device. In<br />
fact, it's almost downright trivial. But according to everything that we know and understand about computation, this trivial device is capable of <em>any</em> computation that can be performed by any other computing device.</p>
<p> The basic idea of the Turing machine is very simple. It's a machine that runs on<br />
top of a <em>tape</em>, which is made up of a long series of little cells, each of which has a single character written on it. The machine is a read/write head that moves over the tape, and which can store a little bit of information. Each step, the<br />
machine looks at the symbol on the cell under the tape head, and based on what<br />
it sees there, and whatever little bit of information it has stored, it decides what to do. The things that it can do are change the information it has store, write a new symbol onto the current tape cell, and move one cell left or right. </p>
<p> That's really it. People who like to make computing sound impressive often have<br />
very complicated explanations of it - but really, that's all there is to it. The point of it was to be simple - and simple it certainly is. And yet, it can do<br /><em>anything</em> that's computable.</p>
<!--more--><p>To really understand how that trivial machine can do computations, it helps to be a bit formal. In formal terms, we talk about a turing machine as a tuple: (S, s<sub>0</sub>, A, T), where:</p>
<ul><li> <b>S</b> is a finite list of possible <em>states</em> that the machine can be in. The state is the information that the machine can store in the head to make decisions. It's a very limited amount of information; you can think of it as either<br />
a specific list of states, or a small set of small numbers. For most Turing machines,<br />
we'll use it as a specific list of states. (You'll see what I mean in a minute.)</li>
<li> s<sub>0</sub> ∈ <b>S</b> is the <em>initial</em> state - the state that the<br />
machine will be in when it starts a computation.</li>
<li> <b>A</b> is the machine's alphabet, which is the set of symbols<br />
which can appear on the machine's tape.</li>
<li> <b>T</b> is the machines <em>transition function</em>. This is the real<br />
heart of the machine, where the computation is defined. It's a function from<br />
the machine state and the alphabet character on the current tape cell to the<br />
action that the machine should take. The action is a triple consisting of<br />
a new value for the machine's state, a character to write onto the current<br />
tape cell, and a direction to move the tape head - either left or right.</li>
</ul><p> So, for example, let's look at a simple machine. This is one of the classic<br />
examples: a Turing machine that does subtraction using <em>unary</em> numbers. A unary<br />
number "N" is written as a series of N 1s. For the program, we'll give the machine an<br />
input in the format "N-M=" written onto the tape; after running the machine, the tape<br />
will contain the value of M subtracted from N. So, for example, we could use "1111-11="<br />
as an input; the output would be "11".</p>
<p> The alphabet is the characters "1", " " (blank space), "-" (minus sign), and "=" (equal sign). The machine has four states: {"scanright", "eraseone", "subone", "skip"}. It starts in the state "scanright". It's transition function is given by the following table:</p>
<table border="2"><tr><th>FromState</th>
<th>Symbol</th>
<th>ToState</th>
<th>WriteChar</th>
<th>Dir</th>
</tr><tr><td>scanright</td>
<td>space</td>
<td>scanright</td>
<td>space</td>
<td>right</td>
</tr><tr><td>scanright</td>
<td>1</td>
<td>scanright</td>
<td>1</td>
<td>right</td>
</tr><tr><td>scanright</td>
<td>minus</td>
<td>scanright</td>
<td>minus</td>
<td>right</td>
</tr><tr><td>scanright</td>
<td>equal</td>
<td>eraseone</td>
<td>space</td>
<td>left</td>
</tr><tr><td>eraseone</td>
<td>1</td>
<td>subone</td>
<td>equal</td>
<td>left</td>
</tr><tr><td>eraseone</td>
<td>minus</td>
<td>HALT</td>
<td>space</td>
<td>n/a</td>
</tr><tr><td>subone</td>
<td>1</td>
<td>subone</td>
<td>1</td>
<td>left</td>
</tr><tr><td>subone</td>
<td>minus</td>
<td>skip</td>
<td>minus</td>
<td>left</td>
</tr><tr><td>skip</td>
<td>space</td>
<td>skip</td>
<td>space</td>
<td>left</td>
</tr><tr><td>skip</td>
<td>1</td>
<td>scanright</td>
<td>space</td>
<td>right</td>
</tr></table><p> What this machine does is move to the right until it sees the equal sign; it erases the equal sign and moves to the left, erases one digit off of the second number, and replaces it with the equal sign (so the second number is reduced by one, and the equal sign is moved over one position). Then it scans back to the minus sign (which separates the two numbers), and erases one digit of the first number, and then switches back to scanning to the right for the equal. So one at a time, it erases one digit from each of the two numbers. When it reaches the equal sign, and starts going back to erase a digit from the second number, and hits the "-" before it finds a digit, that means its done, so it stops. Let's look at a simple execution trace. In the trace, I'll write the<br />
machine state, followed by a colon, followed by the tape contents surrounded by "[]", with the current tape cell surrounded by "{}".</p>
<pre>
scanright: [ {1}1111111-111= ]"
scanright: [ 1{1}111111-111= ]"
scanright: [ 11{1}11111-111= ]"
scanright: [ 111{1}1111-111= ]"
scanright: [ 1111{1}111-111= ]"
scanright: [ 11111{1}11-111= ]"
scanright: [ 111111{1}1-111= ]"
scanright: [ 1111111{1}-111= ]"
scanright: [ 11111111{-}111= ]"
scanright: [ 11111111-{1}11= ]"
scanright: [ 11111111-1{1}1= ]"
scanright: [ 11111111-11{1}= ]"
scanright: [ 11111111-111{=} ]"
eraseone : [ 11111111-11{1} ]"
subone : [ 11111111-1{1}= ]"
subone : [ 11111111-{1}1= ]"
subone : [ 11111111{-}11= ]"
skip : [ 1111111{1}-11= ]"
scanright: [ 1111111 {-}11= ]"
scanright: [ 1111111 -{1}1= ]"
scanright: [ 1111111 -1{1}= ]"
scanright: [ 1111111 -11{=} ]"
eraseone : [ 1111111 -1{1} ]"
subone : [ 1111111 -{1}= ]"
subone : [ 1111111 {-}1= ]"
skip : [ 1111111{ }-1= ]"
skip : [ 111111{1} -1= ]"
scanright: [ 111111 { }-1= ]"
scanright: [ 111111 {-}1= ]"
scanright: [ 111111 -{1}= ]"
scanright: [ 111111 -1{=} ]"
eraseone : [ 111111 -{1} ]"
subone : [ 111111 {-}= ]"
skip : [ 111111 { }-= ]"
skip : [ 111111{ } -= ]"
skip : [ 11111{1} -= ]"
scanright: [ 11111 { } -= ]"
scanright: [ 11111 { }-= ]"
scanright: [ 11111 {-}= ]"
scanright: [ 11111 -{=} ]"
eraseone : [ 11111 {-} ]"
Halt: [ 11111 { }- ]"
</pre><p> See, it works! </p>
<p> One really important thing to understand here is that we <em>do not have a program</em>. What we just did was define a Turing machine that does subtraction. The machine <em>does not</em> take any instructions: the states and the transition function are an <em>intrinsic</em> part of the machine. So the <em>only</em> thing this machine can do is to subtract.</p>
<p> The real genius of Turing wasn't just defining this simple computing machine. It was realizing the concept of the program: you could design a Turing machine whose input tape contained <em>a description of a Turing machine</em> - that is a <em>program</em> - followed by an input to the program. This single machine - the <em>Universal</em> Turing machine - could simulate <em>any</em> other Turing machine; one machine which could perform any computation.</p>
<p> Turing machines are a lot of fun to play with. Figuring out how to do things<br />
can be fascinating. Figuring out how to define a Universal turing machine's transition function is an amazing thing to do; it's astonishing how simple the universal machine can be!</p>
<p> For your enjoyment, I've implemented a Turing machine programming language. You feed it a Turing machine description, and an input string, and it will give you a<br />
trace of the machines execution like the one above. Here's the specification of the<br />
subtraction machine written in my little Turing language:</p>
<pre>
states { "scanright" "eraseone" "subtractOneFromResult" "skipblanks" } initial "scanright"
alphabet { '1' ' ' '=' '-' } blank ' '
trans from "scanright" to "scanright" on (' ') write ' ' move right
trans from "scanright" to "scanright" on ('1') write '1' move right
trans from "scanright" to "scanright" on ('-') write '-' move right
trans from "scanright" to "eraseone" on ('=') write ' ' move left
trans from "eraseone" to "subtractOneFromResult" on ('1') write '=' move left
trans from "eraseone" to "Halt" on ('-') write ' ' move left
trans from "subtractOneFromResult" to "subtractOneFromResult" on ('1') write '1' move left
trans from "subtractOneFromResult" to "skipblanks" on ('-') write '-' move left
trans from "skipblanks" to "skipblanks" on (' ') write ' ' move left
trans from "skipblanks" to "scanright" on ('1') write ' ' move right
</pre><p> I think it's pretty clear as a syntax, but it still needs explanation.</p>
<ul><li> The first line declares the possible states of the machine, and what state it starts in. This machine has four possible states - "scanright", "eraseone", "subtractOneFromResult", and "skipblanks". When the machine starts, it will be in the state "skipright".</li>
<li> The second line declares the set of symbols that can appear on the tape, including what symbol will initially appear on any tape cell whose value wasn't specified by the input. For this machine the symbols are the digit 1, a blank space, the equal sign, and the subtraction symbol; the blank symbol is on any tape cell whose initial value wasn't specified.</li>
<li> After that is a series of transition declarations. Each declaration specifies<br />
what the machine will do for a given pair of initial state and tape symbol. So, for example, if the machine is in state "scanright", and the current tape cell contains the equal-to sign, then the machine will change to state "eraseone", write a blank<br />
onto the tape cell (erasing the "=" sign), and move the tape cell one position<br />
to the left.</li>
</ul><p> Finally, the code. You'll need GHCI to run it; at the moment, it won't work in<br />
Hugs (I don't have the parsing library in my version of Hugs), and I haven't worked<br />
out the linkage stuff to make it work under the GHC compiled mode.</p>
<pre>
--
-- A Simple Turing machine interpreter
-- Copyright 2007 by Mark C. Chu-Carroll
-- markcc@gmail.com
-- http://scienceblogs.com/goodmath
--
-- You can do anything you want with this code so long as you
-- leave this copyright notice intact.
--
module Turing where
import Text.ParserCombinators.Parsec
import qualified Text.ParserCombinators.Parsec.Token as P
import Text.ParserCombinators.Parsec.Language
import System.Environment
data Motion = MoveLeft | MoveRight deriving (Show)
data Transition = Transition String String [Char] Motion Char deriving (Show)
-- from to on move write
data TuringMachine = Machine [String] String [Char] Char [Transition] deriving (Show)
-- states initial alphabet blank transitions
data MachineState = TMState [Char] Char [Char] String deriving (Show)
-- tape-left curcell curstate
getBlankSym :: TuringMachine -> Char
getBlankSym (Machine _ _ _ bl _) = bl
findMatchingTransition :: [Transition] -> String -> Char -> Maybe Transition
findMatchingTransition [] _ _ = Nothing
findMatchingTransition translist state c =
let matches = filter (transitionMatches state c) translist
in case matches of
[] -> Nothing
t:[] -> Just t
_ -> error "Ambiguous transition"
where transitionMatches state c (Transition from to on move write) =
(from == state) && (elem c on)
runTransition :: TuringMachine -> [Char] -> Char -> [Char] -> String -> Transition -> MachineState
runTransition m (l:left) c right state (Transition from tostate on MoveLeft write) =
TMState left l (write:right) tostate
runTransition m left c [] state (Transition from tostate on MoveRight write) =
TMState (write:left) (getBlankSym m) [] tostate
runTransition m left c (r:right) state (Transition from tostate on MoveRight write) =
TMState (write:left) r right tostate
stepMachine :: TuringMachine -> MachineState -> MachineState
stepMachine machine@(Machine _ _ _ _ translist) st@(TMState tapeleft c taperight state) =
let trans = findMatchingTransition translist state c
in case trans of
(Just t) -> runTransition machine tapeleft c taperight state t
Nothing -> error "No applicable transition from state"
getMachineStateString (TMState left c right state) =
(state ++ ":[" ++ (reverse left) ++ "{" ++ [c] ++ "}" ++ right ++ "]")
runTracedMachine :: TuringMachine -> [Char] -> [String]
runTracedMachine tm@(Machine states initial alphabet blank transitions) (t:tape) =
runTracedMachineSteps tm (TMState [] t tape initial) where
runTracedMachineSteps machine state =
let newstate@(TMState left c right st) = stepMachine machine state
in if st == "Halt"
then [getMachineStateString newstate]
else let trace=runTracedMachineSteps machine newstate
in ((getMachineStateString newstate):trace)
runMachine :: TuringMachine -> [Char] -> [Char]
runMachine tm@(Machine states initial alphabet blank transitions) (t:tape) =
runMachineSteps tm (TMState [] t tape initial) where
runMachineSteps machine state =
let newstate@(TMState left c right st) = stepMachine machine state
in if st == "Halt"
then (concat [(reverse left), [c], right])
else runMachineSteps machine newstate
---------------------------------------------------------------------------
-- Parsing code - implemented using the Parsec monadic parser library.
---------------------------------------------------------------------------
-- Basic setup stuff - use a standard haskell style lexer; set up the reserved words
-- and symbols in the lexer.
lexer :: P.TokenParser ()
lexer = P.makeTokenParser (haskellDef
{ P.reservedNames = ["states","alphabet","trans","from","to","on","write","move","left","right","initial","blank"] })
reserved = P.reserved lexer
symbol = P.symbol lexer
braces = P.braces lexer
parens = P.parens lexer
charlit = P.charLiteral lexer
strlit = P.stringLiteral lexer
ident = P.identifier lexer
whitespace = P.whiteSpace lexer
states = reserved "states"
alphabet = reserved "alphabet"
trans = reserved "trans"
from = reserved "from"
to = reserved "to"
on = reserved "on"
write = reserved "write"
move = reserved "move"
initial = reserved "initial"
blank = reserved "blank"
left = do { reserved "left"
; return MoveLeft
}
right = do { reserved "right"
; return MoveRight
}
-- statesDecl ::= "states" "{" strlit+ "}" "initial" strlit
statesDecl = do { states
; strlst right)
; return (Transition fromState targetState chrs dir wrchar)
}
-- machine ::= statesDecl alphaDecl transDecl+
machine = do { (i,sts) Parser a -> String -> IO a
run p input
= case (parse p "" input) of
Left err -> do{ putStr "parse error at "
; print err
; error "Parse error"
}
Right x -> return x
runTParser :: String -> IO TuringMachine
runTParser input =
run (do { whitespace
; x String -> IO ([String])
runTracedMachineOnString m str =
do
tm String -> IO String
runMachineOnString m str =
do
tm do
print "Enter input for parser:"
s do
print (concat ["Parse error"])
runFromFile :: String -> IO ()
runFromFile filename =
do
m do
print "Enter input for parser:"
s do
print (concat ["Parse error"])
</pre></div>
<span><a title="View user profile." href="https://scienceblogs.com/goodmath" lang="" about="https://scienceblogs.com/goodmath" typeof="schema:Person" property="schema:name" datatype="" xml:lang="">goodmath</a></span>
<span>Sat, 02/03/2007 - 12:38</span>
<div class="field field--name-field-blog-categories field--type-entity-reference field--label-inline">
<div class="field--label">Categories</div>
<div class="field--items">
<div class="field--item"><a href="https://scienceblogs.com/channel/free-thought" hreflang="en">Free Thought</a></div>
</div>
</div>
Sat, 03 Feb 2007 17:38:02 +0000goodmath92252 at https://scienceblogs.com