The TravelTree Monad is a monad proposed and designed by Paolo Martini (xerox), and coded by David House (davidhouse). It is based on the State monad and is used for navigating around data structures, using the concept of TheZipper.

As the only zipper currently available is for binary trees, this is what most of the article will be centred around.

Mutation

These are direct front-doors for State's get, put and modify, and all three return the subtree after any applicable modifications.

Exit points

To get out of the monad, use traverse:

traverse::Treea->TravelTreea->Treea

Again, this is just a front-door for evalState, with an initial state of (tt,Top) where tt is the TravelTree passed in.

Examples

The following examples use as the example tree:

t=Branch(Branch(Branch(Leaf1)(Leaf2))(Leaf3))(Branch(Leaf4)(Leaf5))

The example tree

A simple path

This is a very simple example showing how to use the movement functions:

leftLeftRight::TravelTreealeftLeftRight=doleftleftright

Result of evaluation:

*Tree> t `traverse` leftLeftRight
Leaf 2

Tree reverser

This is a more in-depth example showing getTree and putTree, but is still rather contrived as it's easily done without the zipper (the zipper-less version is shown below).

The algorithm reverses the tree, in the sense that at every branch, the two subtrees are swapped over.

revTree::Treea->TreearevTreet=t`traverse`revTree'whererevTree'::TravelTreearevTree'=dot<-getTreecasetofBranch__->doleftl'<-revTree'uprightr'<-revTree'upputTree$Branchr'l'Leafx->return$Leafx-- without using the zipper:revTreeZipless::Treea->TreearevTreeZipless(Leafx)=LeafxrevTreeZipless(Branchxsys)=Branch(revTreeZiplessys)(revTreeZiplessxs)

Generalisation

treeComb::(a->Treea)-- what to put at leaves->(Treea->Treea->Treea)-- what to put at branches->(Treea->Treea)-- combinator functiontreeCombleafbranch=\t->t`traverse`treeComb'wheretreeComb'=dot<-getTreecasetofBranch__->doleftl'<-treeComb'uprightr'<-treeComb'upputTree$branchl'r'Leafx->return$leafx

revTree is then easy:

revTreeZipper::Treea->TreearevTreeZipper=treeCombLeaf(flipBranch)

It turns out this is a fairly powerful combinator. As with revTree, it can change the structure of a tree. Here's another example which turns a tree into one where siblings are sorted, i.e. given a Branchlr, if l and r are leaves, then the value of l is less than or equal to that of r. Also, if one of l or r is a Branch and the other a Leaf, then l is the Leaf and r the Branch:

Code

Here's the Zipper Monad in full:

moduleZipperwhere-- A monad implementing The Zipper. -- http://haskell.org/haskellwiki/ZipperMonad--------------------------------------------------------------------------------importControl.Monad.StateimportControl.Arrow(first,second)dataTreea=Leafa|Branch(Treea)(Treea)deriving(Show,Eq)dataCxta=Top|L(Cxta)(Treea)|R(Treea)(Cxta)deriving(Show)typeLoca=(Treea,Cxta)newtypeTravelta=Travel{unT::Stateta}deriving(Functor,Monad,MonadStatet)typeTravelTreea=Travel(Loca)(Treea)-- Movement around the tree---- move down a level, through the left branchleft::TravelTreealeft=modifyleft'>>liftMfstgetwhereleft'(Branchlr,c)=(l,Lcr)-- move down a level, through the left branchright::TravelTreearight=modifyright'>>liftMfstgetwhereright'(Branchlr,c)=(r,Rlc)-- move to a node's parentup::TravelTreeaup=modifyup'>>liftMfstgetwhereup'(t,Lcr)=(Branchtr,c)up'(t,Rlc)=(Branchlt,c)-- move to the top nodetop::TravelTreeatop=modify(second$constTop)>>liftMfstget-- Mutation of the tree---- modify the subtree at the current nodemodifyTree::(Treea->Treea)->TravelTreeamodifyTreef=modify(firstf)>>liftMfstget-- put a new subtree at the current nodeputTree::Treea->TravelTreeaputTreet=modifyTree$constt-- get the current node and its descendantsgetTree::TravelTreeagetTree=modifyTreeid-- works because modifyTree returns the 'new' tree-- Exit points---- get out of the monadtraverse::Treea->TravelTreea->Treeatraversettt=evalState(unTtt)(t,Top)