Data.Tree.Zipper in the standard libraries

Hello Guys, We have Data.Tree in the standard libraries for a long time but for some reason we still don't have standard implementation for Zipper. I wrote recently one implementation for Yi but there are many other versions hanging around. At least I know for some. I propose to add one in the standard libraries i.e. the "containers" package. The version that I use currently is here: http://code.haskell.org/yi/Data/Tree/Zipper.hs If you would like to do code review I will be happy to hear comments. After the API is settled down I will write test cases also. One thing that is worying me is that the current version uses State monad which is in the "mtl" package while the natural place for Data.Tree.Zipper is in "containers". This will create an extra dependency. Is this acceptable? Regards, Krasimir

Hi Krasimir,
I had a long exchange with chessguy about this interface, suggesting a
significant change in style, simplifying the type. (Incidentally, the
change removed the State and hence mtl dependence.)
The conversation is on http://tunes.org/~nef/logs/haskell/08.05.17, starting
with "12:08:11 <chessguy> w00t!" and really picking up with "<conal>
chessguy: something smells funny ...".
Here's a summary of the conversation, though I encourage you to read the
whole thing:
* Every definition of tp 'State (TreeLoc a) a', does a getLabel at the end
(except getLabel).
* Often users of those movement functions discard the result.
* Simpler and more orthogonal would be remove the getLabel and return 'State
(TreeLoc a) ()' instead.
* Now remove that return value altogether, simplifying the type of zipper
movements to just 'TreeLoc a -> TreeLoc a'. Then they compose nicely with
(.), having id as identity.
* Simplify the type of getLabel to just 'TreeLoc a -> a'. Now no more
State.
Cheers, - Conal
On Thu, May 22, 2008 at 12:52 PM, Krasimir Angelov
Hello Guys,
We have Data.Tree in the standard libraries for a long time but for some reason we still don't have standard implementation for Zipper. I wrote recently one implementation for Yi but there are many other versions hanging around. At least I know for some. I propose to add one in the standard libraries i.e. the "containers" package. The version that I use currently is here:
http://code.haskell.org/yi/Data/Tree/Zipper.hs
If you would like to do code review I will be happy to hear comments. After the API is settled down I will write test cases also. One thing that is worying me is that the current version uses State monad which is in the "mtl" package while the natural place for Data.Tree.Zipper is in "containers". This will create an extra dependency. Is this acceptable?
Regards, Krasimir _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

* Every definition of tp
I meant "of type", forgetting that my emacs abbrevs don't expand in gmail.
On Thu, May 22, 2008 at 2:13 PM, Conal Elliott
Hi Krasimir,
I had a long exchange with chessguy about this interface, suggesting a significant change in style, simplifying the type. (Incidentally, the change removed the State and hence mtl dependence.)
The conversation is on http://tunes.org/~nef/logs/haskell/08.05.17http://tunes.org/%7Enef/logs/haskell/08.05.17, starting with "12:08:11 <chessguy> w00t!" and really picking up with "<conal> chessguy: something smells funny ...".
Here's a summary of the conversation, though I encourage you to read the whole thing:
* Every definition of tp 'State (TreeLoc a) a', does a getLabel at the end (except getLabel). * Often users of those movement functions discard the result. * Simpler and more orthogonal would be remove the getLabel and return 'State (TreeLoc a) ()' instead. * Now remove that return value altogether, simplifying the type of zipper movements to just 'TreeLoc a -> TreeLoc a'. Then they compose nicely with (.), having id as identity. * Simplify the type of getLabel to just 'TreeLoc a -> a'. Now no more State.
Cheers, - Conal
On Thu, May 22, 2008 at 12:52 PM, Krasimir Angelov
wrote: Hello Guys,
We have Data.Tree in the standard libraries for a long time but for some reason we still don't have standard implementation for Zipper. I wrote recently one implementation for Yi but there are many other versions hanging around. At least I know for some. I propose to add one in the standard libraries i.e. the "containers" package. The version that I use currently is here:
http://code.haskell.org/yi/Data/Tree/Zipper.hs
If you would like to do code review I will be happy to hear comments. After the API is settled down I will write test cases also. One thing that is worying me is that the current version uses State monad which is in the "mtl" package while the natural place for Data.Tree.Zipper is in "containers". This will create an extra dependency. Is this acceptable?
Regards, Krasimir _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Aha. I see the point. The current design was influenced from some
other implementation that I spot somewhere in the net. I will change
it but this reminds me of another problem. Most movement functions
raise error if they can't do the movement. This is convenient if you
somehow know that the direction in which you go always exists.
Alternatively I can use monad with failure. In other words, there are
two possibilities:
1. Use error ".." and types like: TreeLoc a -> TreeLoc a
2. Use monad and type like: Monad m => TreeLoc a -> m (TreeLoc a)
In the second case some one have to do something like this to check
whether the operation is successful:
case left loc of
Noting -> error "something gone wrong"
Just loc -> ...
In the first case there is no way to check for success but someone can
use precondition:
if isFirst loc
then do_something (left loc)
else i_cannot_go_left
In my opinion 1 looks nicer but 2 also have advantages. What do you think?
Regards,
Krasimir
On Fri, May 23, 2008 at 12:33 AM, Conal Elliott
* Every definition of tp
I meant "of type", forgetting that my emacs abbrevs don't expand in gmail.
On Thu, May 22, 2008 at 2:13 PM, Conal Elliott
wrote: Hi Krasimir,
I had a long exchange with chessguy about this interface, suggesting a significant change in style, simplifying the type. (Incidentally, the change removed the State and hence mtl dependence.)
The conversation is on http://tunes.org/~nef/logs/haskell/08.05.17, starting with "12:08:11 <chessguy> w00t!" and really picking up with "<conal> chessguy: something smells funny ...".
Here's a summary of the conversation, though I encourage you to read the whole thing:
* Every definition of tp 'State (TreeLoc a) a', does a getLabel at the end (except getLabel). * Often users of those movement functions discard the result. * Simpler and more orthogonal would be remove the getLabel and return 'State (TreeLoc a) ()' instead. * Now remove that return value altogether, simplifying the type of zipper movements to just 'TreeLoc a -> TreeLoc a'. Then they compose nicely with (.), having id as identity. * Simplify the type of getLabel to just 'TreeLoc a -> a'. Now no more State.
Cheers, - Conal
On Thu, May 22, 2008 at 12:52 PM, Krasimir Angelov
wrote: Hello Guys,
We have Data.Tree in the standard libraries for a long time but for some reason we still don't have standard implementation for Zipper. I wrote recently one implementation for Yi but there are many other versions hanging around. At least I know for some. I propose to add one in the standard libraries i.e. the "containers" package. The version that I use currently is here:
http://code.haskell.org/yi/Data/Tree/Zipper.hs
If you would like to do code review I will be happy to hear comments. After the API is settled down I will write test cases also. One thing that is worying me is that the current version uses State monad which is in the "mtl" package while the natural place for Data.Tree.Zipper is in "containers". This will create an extra dependency. Is this acceptable?
Regards, Krasimir _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Fri, May 23, 2008 at 09:03:29AM +0200, Krasimir Angelov wrote:
Alternatively I can use monad with failure. In other words, there are two possibilities:
1. Use error ".." and types like: TreeLoc a -> TreeLoc a 2. Use monad and type like: Monad m => TreeLoc a -> m (TreeLoc a)
I'd suggest that TreeLoc a -> Maybe (TreeLoc a) is better than the second version. The problem with using Monad(fail) is that it hides possible runtime errors among testable conditions. With 1. you can at least search the code for occurrences of the dangerous function. With the Monad version you need to consider the type of each use to know whether it is dangerous.

The monads design is used in Data.Map i.e.
lookup :: (Monad m, Ord k) => k -> Map k a -> m a
and I think that this will be more consistent.
On 5/23/08, Ross Paterson
On Fri, May 23, 2008 at 09:03:29AM +0200, Krasimir Angelov wrote:
Alternatively I can use monad with failure. In other words, there are two possibilities:
1. Use error ".." and types like: TreeLoc a -> TreeLoc a 2. Use monad and type like: Monad m => TreeLoc a -> m (TreeLoc a)
I'd suggest that TreeLoc a -> Maybe (TreeLoc a) is better than the second version. The problem with using Monad(fail) is that it hides possible runtime errors among testable conditions. With 1. you can at least search the code for occurrences of the dangerous function. With the Monad version you need to consider the type of each use to know whether it is dangerous. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Krasimir Angelov wrote:
The monads design is used in Data.Map i.e.
lookup :: (Monad m, Ord k) => k -> Map k a -> m a
which is widely considered a poor design decision and a wart on Data.Map. :-) Seriously, if you don't return a useful error message, then Maybe is as good as it gets, why not use it? (And there really is only one kind of error possible here, in each case) 'fail' only adds information in the case it has a useful error message (in which case I'd encourage MonadError m =>) Jules

From: haskell-cafe-bounces@haskell.org [mailto:haskell-cafe-bounces@haskell.org] On Behalf Of Jules Bean
Krasimir Angelov wrote:
The monads design is used in Data.Map i.e.
lookup :: (Monad m, Ord k) => k -> Map k a -> m a
which is widely considered a poor design decision and a wart on Data.Map.
Seriously, if you don't return a useful error message, then Maybe is as good as it gets, why not use it?
(And there really is only one kind of error possible here, in each case)
'fail' only adds information in the case it has a useful error message (in which case I'd encourage MonadError m =>)
Is there a reason not to recommend Either, if you want to return error information? It is an instance of MonadError. Alistair ***************************************************************** Confidentiality Note: The information contained in this message, and any attachments, may contain confidential and/or privileged material. It is intended solely for the person(s) or entity to which it is addressed. Any review, retransmission, dissemination, or taking of any action in reliance upon this information by persons or entities other than the intended recipient(s) is prohibited. If you received this in error, please contact the sender and delete the material from any computer. *****************************************************************

That is annoying. I remember that there were people arguing that Monad
should be used instead of Maybe but I don't remember the reasons.
Personaly I feel quite happy with Maybe and I am using it in my own
code. There should be a consistent approach in the different libraries
or at least in a single package like "containers".
While googling for Zipper I also found this article:
http://citeseer.ist.psu.edu/hinze01web.html
Is this better alternative to Zipper? Have someone better experience with it?
Regards,
Krasimir
On 5/23/08, Jules Bean
Krasimir Angelov wrote:
The monads design is used in Data.Map i.e.
lookup :: (Monad m, Ord k) => k -> Map k a -> m a
which is widely considered a poor design decision and a wart on Data.Map.
:-)
Seriously, if you don't return a useful error message, then Maybe is as good as it gets, why not use it?
(And there really is only one kind of error possible here, in each case)
'fail' only adds information in the case it has a useful error message (in which case I'd encourage MonadError m =>)
Jules

I think that MonadZero m => ... is better than just Maybe. If you can have more general solution for free, why fight it? On 23 May 2008, at 14:55, Jules Bean wrote:
Krasimir Angelov wrote:
The monads design is used in Data.Map i.e. lookup :: (Monad m, Ord k) => k -> Map k a -> m a
which is widely considered a poor design decision and a wart on Data.Map.
:-)
Seriously, if you don't return a useful error message, then Maybe is as good as it gets, why not use it?
(And there really is only one kind of error possible here, in each case)
'fail' only adds information in the case it has a useful error message (in which case I'd encourage MonadError m =>)
Jules _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Fri, May 23, 2008 at 10:55 AM, Jules Bean
Krasimir Angelov wrote:
The monads design is used in Data.Map i.e.
lookup :: (Monad m, Ord k) => k -> Map k a -> m a
which is widely considered a poor design decision and a wart on Data.Map.
It is? Can you point to somewhere explaining that? I rather liked that idiom. Luke

On 2008 May 23, at 13:34, Luke Palmer wrote:
On Fri, May 23, 2008 at 10:55 AM, Jules Bean
wrote: Krasimir Angelov wrote:
The monads design is used in Data.Map i.e.
lookup :: (Monad m, Ord k) => k -> Map k a -> m a
which is widely considered a poor design decision and a wart on Data.Map.
It is? Can you point to somewhere explaining that? I rather liked that idiom.
I'd argue that the poor design decision was killing MonadZero, and the type of Data.Map.lookup is a hackaround. -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

Using 'monad' here makes it easier to make a mistake with the code, as it permits new kinds of unexpected failure. This is Haskell, we should use Maybe. And users that want it can lift Maybe a -> m a -- Don (-1) for new uses of fail in place of Nothing. kr.angelov:
The monads design is used in Data.Map i.e.
lookup :: (Monad m, Ord k) => k -> Map k a -> m a
and I think that this will be more consistent.
On 5/23/08, Ross Paterson
wrote: On Fri, May 23, 2008 at 09:03:29AM +0200, Krasimir Angelov wrote:
Alternatively I can use monad with failure. In other words, there are two possibilities:
1. Use error ".." and types like: TreeLoc a -> TreeLoc a 2. Use monad and type like: Monad m => TreeLoc a -> m (TreeLoc a)
I'd suggest that TreeLoc a -> Maybe (TreeLoc a) is better than the second version. The problem with using Monad(fail) is that it hides possible runtime errors among testable conditions. With 1. you can at least search the code for occurrences of the dangerous function. With the Monad version you need to consider the type of each use to know whether it is dangerous. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

G'day all.
Quoting Don Stewart
This is Haskell, we should use Maybe.
This is Haskell, more abstract is good. I do agree, though, that Monad is arguably the wrong abstraction. Something like this would arguably be better: class (Functor f) => Fail f where return :: a -> f a fail :: String -> f a (And yes, the String is arguably important; Maybe doesn't give you that.) Cheers, Andrew Bromage
participants (10)
-
ajb@spamcop.net
-
Bayley, Alistair
-
Brandon S. Allbery KF8NH
-
Conal Elliott
-
Don Stewart
-
Jules Bean
-
Krasimir Angelov
-
Luke Palmer
-
Miguel Mitrofanov
-
Ross Paterson