
Hello everyone, There is now a new version of 'monadLib': a library of monad transformers for Haskell. 'monadLib' is a descendent of 'mtl', the monad template library that is distributed with most Haskell implementations. The library web page is at: http://www.csee.ogi.edu/~diatchki/monadLib Comments and feedback are very welcome! -Iavor

On Sun, Mar 19, 2006 at 10:02:57PM -0800, Iavor Diatchki wrote:
There is now a new version of 'monadLib': a library of monad transformers for Haskell. 'monadLib' is a descendent of 'mtl', the monad template library that is distributed with most Haskell implementations. The library web page is at: http://www.csee.ogi.edu/~diatchki/monadLib
Comments and feedback are very welcome!
it looks very interesting. I use the mtl extensively in pretty much everything I do and am interested in any developments with it. a couple things whenM,forEach2_, and forEach3_ should have types whenM :: Monad m => m Bool -> m a -> m () forEach2_ :: Monad m => [a] -> [b] -> (a -> b -> m c) -> m () forEach3_ :: Monad m => [a] -> [b] -> [c] -> (a -> b -> c -> m d) -> m () Also, I am curious why some of the names seem to have been changed from what the mtl provides, it would be nice if it were mostly a drop in replacement as there is substantial mtl code out there. mainly I am thinking of ask -> getR local -> updateR also, it looks like some of the very useful utility routines in the current mtl such as 'asks' arn't included. though, I would be very happy to shorten 'Identity' to 'Id'. having a standard identity newtype is surprising useful all over the place, even when not used as a monad. deriving Typeable for everything would be good too. Also, it looks like some of your monads overlap with what is provided by the Applicative (Idiom) and friends classes to be included in the next version of ghc, perhaps you can make a version of monadLib that builds on those? John -- John Meacham - ⑆repetae.net⑆john⑈

Hello,
On 3/20/06, John Meacham
... it looks very interesting. I use the mtl extensively in pretty much everything I do and am interested in any developments with it. a couple things
whenM,forEach2_, and forEach3_ should have types whenM :: Monad m => m Bool -> m a -> m () forEach2_ :: Monad m => [a] -> [b] -> (a -> b -> m c) -> m () forEach3_ :: Monad m => [a] -> [b] -> [c] -> (a -> b -> c -> m d) -> m () This is quite reasonable for 'forEach23_', and perhaps by symmetry for 'whenM'. One thing about 'whenM' however is that to implement it I'll need to add an extra 'return ()' to the parameter computation. This is probably not a big deal, but given that most of the time the computation used in 'whenM' is already of type 'm ()', I wonder if leaving it to the programmer to add the 'return ()' is not a better idea. What do you think?
Also, I am curious why some of the names seem to have been changed from what the mtl provides, it would be nice if it were mostly a drop in replacement as there is substantial mtl code out there.
mainly I am thinking of ask -> getR local -> updateR
also, it looks like some of the very useful utility routines in the current mtl such as 'asks' arn't included. I didn't add 'asks' because to me it seems redundant. Instead of "asks field" I write "field # getR" (or 'field # ask' if we were to use the 'mtl' terminology). Are there other useful methods
Some of the other names I changed because I didn't like (e.g. I like get/set for state, rather then get/put in 'mtl'). These two (getR,localR) I am not too happy with (also the name of the ReadUpdM class). I picked them because they resemble the state ones, but perhaps the 'mtl' ones are better, although I am not sure about that. Note however that 'monadLib' is not a drop in replacement for 'mtl', as other things differ as well, for example 'monadLib' has a different monad class hierarchy. that I missed?
though, I would be very happy to shorten 'Identity' to 'Id'. having a standard identity newtype is surprising useful all over the place, even when not used as a monad. That's interesting to know. I have not used it for anything else, do you have examples of how you use it?
deriving Typeable for everything would be good too. I have never used 'Typeable', but perhaps I will take a look. I would like 'monadLib' to stay simple, but if people use 'Typeable' on monadic computations I could probably add some instances. Are there any examples of when it is useful to do things like that? Does Hugs support Typeable?
Also, it looks like some of your monads overlap with what is provided by the Applicative (Idiom) and friends classes to be included in the next version of ghc, perhaps you can make a version of monadLib that builds on those? I am aware of that, and once they are released I will probably make use of them, because I think that they have some nice ideas. In the mean time however 'monadLib' provides 'Monad.ForEach' and 'Monad.Combinators'.
Thanks for the comments! -Iavor

On Tue, Mar 21, 2006 at 10:38:06AM -0800, Iavor Diatchki wrote:
On 3/20/06, John Meacham
wrote: also, it looks like some of the very useful utility routines in the current mtl such as 'asks' arn't included. I didn't add 'asks' because to me it seems redundant.
I would like it if all mtl classes had a function that captures the general operation of the monad. In this sense, asks is the essence of the Reader monad. The essense of the State monad, which I have defined for myself many times, is state :: MonadState s m => (s -> (a, s)) -> m a And so on for the others. These could be part of the class (my preference, because the other ops can have default implementations in terms of it) or auxiliary functions. Andrew

Hello,
On 3/21/06, Andrew Pimlott
I would like it if all mtl classes had a function that captures the general operation of the monad. In this sense, asks is the essence of the Reader monad. The essense of the State monad, which I have defined for myself many times, is
state :: MonadState s m => (s -> (a, s)) -> m a
And so on for the others. These could be part of the class (my preference, because the other ops can have default implementations in terms of it) or auxiliary functions.
'monadLib' already contains such functions. Take a look at: http://www.csee.ogi.edu/~diatchki/monadLib/current/doc/html/Monad-Prelude.ht... They are called inReader, inWriter, inState, inExcept. There isn't one for searching because we have 'msum' which does the same, and there isn't one for continuations because I wasn't sure what should be the type. At the moment they are functions and not methods. It is always tricky to decide what to put in the classes, but my feeling is that functions are more appropriate in this case. -Iavor

On Tue, Mar 21, 2006 at 01:03:33PM -0800, Iavor Diatchki wrote:
Hello,
On 3/21/06, Andrew Pimlott
wrote: I would like it if all mtl classes had a function that captures the general operation of the monad. In this sense, asks is the essence of the Reader monad.
'monadLib' already contains such functions.
Oh, nice. In that case, you gave the wrong answer for why you don't have asks, because you do! Andrew

Hello Iavor, Tuesday, March 21, 2006, 9:38:06 PM, you wrote:
whenM :: Monad m => m Bool -> m a -> m () ID> One thing about 'whenM' however is that to implement it I'll need to ID> add an extra ID> 'return ()' to the parameter computation. This is probably not a big deal, but ID> given that most of the time the computation used in 'whenM' is already ID> of type 'm ()', I wonder if leaving it to the programmer to add the ID> 'return ()' is not a better idea. ID> What do you think?
that is a sort of thing that bother me constantly. `when` has the same problem. i as application programmer want to write less unnecessary code ID> Note however that 'monadLib' is not a drop in replacement for 'mtl', ID> as other things differ as well, for example 'monadLib' has a different ID> monad class hierarchy. class hierarchy don't matter for application programs in most cases. may be it's better to have "mtl emulation module" and give it (in ghc 6.6) the same name as mtl module has currently. so new apps will use your library directly and other apps will use it through emulation. i use the same technique for my own Binary-replacing module
deriving Typeable for everything would be good too. ID> I have never used 'Typeable', but perhaps I will take a look. I would ID> like 'monadLib' to stay simple, but if people use 'Typeable' on ID> monadic computations I could probably add some instances. Are there ID> any examples of when it is useful to do things like that? Does Hugs ID> support Typeable?
i think that support for Typeable in any library is a "good form" now, like support for monads itself :)
Also, it looks like some of your monads overlap with what is provided by the Applicative (Idiom) and friends classes to be included in the next version of ghc, perhaps you can make a version of monadLib that builds on those? ID> I am aware of that, and once they are released I will probably make ID> use of them, because I think that they have some nice ideas. In the ID> mean time however 'monadLib' provides 'Monad.ForEach' and ID> 'Monad.Combinators'.
i think that it's better not to wait while they will be released, but after your MonadLib will be stabilized, propose to include it in 6.6 and develop version of lib that is compatible with 6.6 (if your library goal is to replace mtl) -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

Hello,
Thanks for the feedback! Here are my thoughts on the questions you raise...
On 3/22/06, Bulat Ziganshin
whenM :: Monad m => m Bool -> m a -> m () ID> One thing about 'whenM' however is that to implement it I'll need to ID> add an extra ID> 'return ()' to the parameter computation. This is probably not a big deal, but ID> given that most of the time the computation used in 'whenM' is already ID> of type 'm ()', I wonder if leaving it to the programmer to add the ID> 'return ()' is not a better idea. ID> What do you think?
that is a sort of thing that bother me constantly. `when` has the same problem. i as application programmer want to write less unnecessary code
Perhaps 'constantly' is too strong of a statement? I very rarely run into problems like that, because mostly I use the results of computations, but maybe this is just my style. I see some benefit to writing an explicit 'return ()', because it becomes more obvious that we are ignoring the (meaningful, that is not ()) result of a computation.
class hierarchy don't matter for application programs in most cases. may be it's better to have "mtl emulation module" and give it (in ghc 6.6) the same name as mtl module has currently. so new apps will use your library directly and other apps will use it through emulation. i use the same technique for my own Binary-replacing module
i think that support for Typeable in any library is a "good form" now, like support for monads itself :) I am not a big believer in adding things to a library because they might be useful one day. I think that one should first use stuff in
I am not sure why you think that class hierarchies don't matter to programmers. In fact Java programmers seem to like them a lot. All I meant is that because the class hierarchy differs, some of the operations not only have different names, but also they have different types. I am not particularly interested in creating an 'mtl' compatibility module, because I think it will create confusion (the libraries are similar enough as it is). People that want to use 'mtl' can use it directly, using 'monadLib' through an 'mtl' compatibility mode does not buy you anything (perhaps the WriterT in 'monadLib' is faster, but I am not sure how big of a deal that is). I think the main benefit of 'monadLib' is that it has a nicer interface than 'mtl'. their programs, and once they notice that some bit of code appears a lot, than perhaps it should be added to a library. For example, I wrote very many monads before I started using monad transformers, and all the instances were the same -- very boring. These days I very rarely create instances of the monad class. So I think that I could add support for 'Typeable', but I'd rather do it when I know of some compelling examples.
ID> I am aware of that, and once they are released I will probably make ID> use of them, because I think that they have some nice ideas. In the ID> mean time however 'monadLib' provides 'Monad.ForEach' and ID> 'Monad.Combinators'.
i think that it's better not to wait while they will be released, but after your MonadLib will be stabilized, propose to include it in 6.6 and develop version of lib that is compatible with 6.6 (if your library goal is to replace mtl)
I can't really use the stuff until it is released, unless (I guess) I copy-and-pasted it into my library, which does not seem like the right thing to do. While I wouldn't mind the library replacing 'mtl' (this was the original idea from some time ago) I am not sure if that is feasible (backward compatibility and what not). Also I think that libraries should be used a little more before they become "standard", so far I think I am the only real user of 'monadLib'. By the way what do you mean "compatible with 6.6"? I don't have a copy of that because it is not released (and if I understand correctly will not be for some time yet). My library uses only what I consider "standard" extensions to Haskell'98 (some rank-2 poly, and multi-parameter type classes with functional dependencies). Are any of these likely to go away in 6.6? -Iavor

Iavor Diatchki wrote:
whenM :: Monad m => m Bool -> m a -> m ()
I see some benefit to writing an explicit 'return ()', because it becomes more obvious that we are ignoring the (meaningful, that is not ()) result of a computation.
What about this? *> when :: Monad m => m a -> Bool -> m (Maybe a) It doesn't ignore any meaningful result, ignoring the (Maybe a) would be explicit through the use of (>>). The arguments are swapped, so it can more easily replace whenM: *> whenM p a ~~ p >>= when a Granted, it reads clumsily. Maybe there's a place for whenM. I still like the swapped arguments, as the infix version will read more natural. Udo. PS:
I am not sure why you think that class hierarchies don't matter to programmers. In fact Java programmers seem to like them a lot.
They probably don't have anything else to like about their language ;-) -- Never argue with an idiot. They drag you down to their level, then beat you with experience.

Hello,
On 3/22/06, Udo Stenzel
What about this?
*> when :: Monad m => m a -> Bool -> m (Maybe a)
It doesn't ignore any meaningful result, ignoring the (Maybe a) would be explicit through the use of (>>).
I am not sure if this would be useful. If you cared about the result you would have to write: do { x <- when p m; case x of { Just a -> e1; Nothing -> e2. } This is not good, because you are performing two tests (is 'p' true, did we get a 'Just'), when there should be only one. Furthermore this seems difficult to optmize away. A simpler and (probably) more efficient way to write the above is: do { if p then do { a <- m; e1} else e2 } I think that 'when' (and 'whenM') are usually used when the computation does not return a result, so it is reasobale that the final result is of type 'm ()". In some (rare) situation we really mean to ignore the result of the body of the 'when'. The main point of this discussion has been: (i) Should the ignoring be done silently in the library, or (ii) should the programmer be explicit (i.e., write 'm >> return ()'). I don't think either way matters much, but I tend to lean towards the status quo, i.e. programmers have to be explicit. -Iavor

Iavor Diatchki wrote:
*> when :: Monad m => m a -> Bool -> m (Maybe a)
I am not sure if this would be useful. If you cared about the result you would have to write: do { x <- when p m; case x of { Just a -> e1; Nothing -> e2. }
...unless you wanted the result in the form of a Maybe. Since there really is no other sensible choice for the "False" branch, I contend that _if_ you want the result at all, you want is as a maybe. If not, well, then ignoring (Maybe a) isn't any harder than ignoring (); (>>) already accomplishes that.
A simpler and (probably) more efficient way to write the above is: do { if p then do { a <- m; e1} else e2 }
But usually p comes from a monadic computation, so in reality you need *> do { p' <- p ; if p' then do { a <- m ; e1 } else e2 } which I find excessively ugly. Actually this is not a case for when, it's one for cond: *> cond t f True = t *> cond t f False = f *> p >>= cond (do { a <- m ; e1 }) e2
The main point of this discussion has been: (i) Should the ignoring be done silently in the library, or (ii) should the programmer be explicit (i.e., write 'm >> return ()').
I just think that both options are wrong. (iii) should the programmer be explicit (ie. write 'when p m >>') Udo. -- Das Elend hat viele Gesichter, wie gefällt Ihnen meins?

Hello,
On 3/23/06, Udo Stenzel
Iavor Diatchki wrote: ... But usually p comes from a monadic computation, so in reality you need
*> do { p' <- p ; if p' then do { a <- m ; e1 } else e2 }
which I find excessively ugly. Actually this is not a case for when, it's one for cond:
*> cond t f True = t *> cond t f False = f
*> p >>= cond (do { a <- m ; e1 }) e2
In the Monad.Combinatros module of 'monadLib' there is the 'ifM' combinator which is kind of like the above: ifM :: Monad m => m Bool -> m a -> m a -> m a You could write: ifM p (do { a <- m; e1 }) e2 The combinator 'whenM' is just a special case: whenM p m = ifM p m (return ()) . -Iavor

Hello Iavor, Wednesday, March 22, 2006, 8:49:29 PM, you wrote:
whenM :: Monad m => m Bool -> m a -> m () ID> One thing about 'whenM' however is that to implement it I'll need to ID> add an extra ID> 'return ()' to the parameter computation. This is probably not a big deal, but ID> given that most of the time the computation used in 'whenM' is already ID> of type 'm ()', I wonder if leaving it to the programmer to add the ID> 'return ()' is not a better idea. ID> What do you think?
that is a sort of thing that bother me constantly. `when` has the same problem. i as application programmer want to write less unnecessary code
ID> Perhaps 'constantly' is too strong of a statement? I very rarely run ID> into problems like that, because mostly I use the results of ID> computations, but maybe this is just my style. I see some benefit to ID> writing an explicit 'return ()', because it becomes more obvious that ID> we are ignoring the (meaningful, that is not ()) result of a ID> computation. we say here about type of `when` and `whenM`: either whenM :: Monad m => m Bool -> m a -> m () or whenM :: Monad m => m Bool -> m () -> m () in both cases value of second argument cannot be used. third possible variant is: whenM :: Monad m => m Bool -> m a -> m (Maybe a) and whenM_ :: Monad m => m Bool -> m a -> m () the same can be applied to the ifM/ifM_
class hierarchy don't matter for application programs in most cases. may be it's better to have "mtl emulation module" and give it (in ghc 6.6) the same name as mtl module has currently. so new apps will use your library directly and other apps will use it through emulation. i use the same technique for my own Binary-replacing module
ID> I am not sure why you think that class hierarchies don't matter to ID> programmers. In fact Java programmers seem to like them a lot. All I ID> meant is that because the class hierarchy differs, some of the ID> operations not only have different names, but also they have different ID> types. i mean that it's impossible to do application programming without knowing names of operations in used libs, but it's possible without knowing of class hierarchy in used libs and even class names at all. i personally prefer in application programming to omit type declarations completely ID> I am not particularly interested in creating an 'mtl' compatibility ID> module, because I think it will create confusion (the libraries are ID> similar enough as it is). People that want to use 'mtl' can use it ID> directly, using 'monadLib' through an 'mtl' compatibility mode does ID> not buy you anything (perhaps the WriterT in 'monadLib' is faster, but ID> I am not sure how big of a deal that is). I think the main benefit of ID> 'monadLib' is that it has a nicer interface than 'mtl'. if so, then you can skip this
i think that support for Typeable in any library is a "good form" now, like support for monads itself :) ID> I am not a big believer in adding things to a library because they ID> might be useful one day. I think that one should first use stuff in ID> their programs, and once they notice that some bit of code appears a ID> lot, than perhaps it should be added to a library. For example, I ID> wrote very many monads before I started using monad transformers, and ID> all the instances were the same -- very boring. These days I very ID> rarely create instances of the monad class. So I think that I could ID> add support for 'Typeable', but I'd rather do it when I know of some ID> compelling examples.
well, i can say in another way - it's the same as adding instances of read/show/eq/ord for data structures. Typeable is used for generic/dynamic programming, see "scrap your boilerplate" papers
ID> I am aware of that, and once they are released I will probably make ID> use of them, because I think that they have some nice ideas. In the ID> mean time however 'monadLib' provides 'Monad.ForEach' and ID> 'Monad.Combinators'.
i think that it's better not to wait while they will be released, but after your MonadLib will be stabilized, propose to include it in 6.6 and develop version of lib that is compatible with 6.6 (if your library goal is to replace mtl)
ID> I can't really use the stuff until it is released, unless (I guess) I ID> copy-and-pasted it into my library, which does not seem like the right ID> thing to do. While I wouldn't mind the library replacing 'mtl' (this ID> was the original idea from some time ago) I am not sure if that is ID> feasible (backward compatibility and what not). Also I think that ID> libraries should be used a little more before they become "standard", ID> so far I think I am the only real user of 'monadLib'. ID> By the way what do you mean "compatible with 6.6"? I don't have a ID> copy of that because it is not released (and if I understand correctly ID> will not be for some time yet). My library uses only what I consider ID> "standard" extensions to Haskell'98 (some rank-2 poly, and ID> multi-parameter type classes with functional dependencies). Are any ID> of these likely to go away in 6.6? Applicative and other classes, written by Ross Paterson, are already included in current HEAD version, i.e. 6.5 beta. if you are interesting in including MonadLib as part of ghc 6.6, you should write appropriate version of your library before ghc 6.6 will be released. if you don't think about this, may be users will force you in this direction :) -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

On Tue, Mar 21, 2006 at 10:38:06AM -0800, Iavor Diatchki wrote:
Some of the other names I changed because I didn't like (e.g. I like get/set for state, rather then get/put in 'mtl'). These two (getR,localR) I am not too happy with (also the name of the ReadUpdM class). I picked them because they resemble the state ones, but perhaps the 'mtl' ones are better, although I am not sure about that.
Note however that 'monadLib' is not a drop in replacement for 'mtl', as other things differ as well, for example 'monadLib' has a different monad class hierarchy.
Is there a description anywhere of the differences between the two, and what would be involved in moving one's application from mtl to monadLib?

Hello,
On 3/24/06, Ross Paterson
Is there a description anywhere of the differences between the two, and what would be involved in moving one's application from mtl to monadLib?
No, but such a document would be quite useful and I should probably try to write one, but that might take me a while as I am quite busy at the moment. The good news is that the intreface to 'monadLib' is all in a single place (the module 'Monad.Prelude'), so it is easy to see most of the differences. The web-page (www.csee.ogi.edu/~diatchki/monadLib) also contains a table that lists all the transformers and what operations they support, which would probably be informative to someone familiar with 'mtl'. Also 'monadLib' is very small and has documentation (and examples), so looking through it source code is not unreasonable. -Iavor
participants (6)
-
Andrew Pimlott
-
Bulat Ziganshin
-
Iavor Diatchki
-
John Meacham
-
Ross Paterson
-
Udo Stenzel