www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Monads compared to InputRanges?

reply Shammah Chancellor <anonymous coward.com> writes:
I'm not particularly familiar with the syntax being used in the variet 
of monad examples.   I'm trying to figure out how this is different 
from UFCS on InputRanges.   It seems like std.algorithm implements 
something which accomplished the same thing, but much easier to 
understand?

Can somebody maybe do a compare and contrast for me?

-Shammah
Dec 02 2013
next sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 12/02/2013 06:45 PM, Shammah Chancellor wrote:

 I'm not particularly familiar with the syntax being used in the variet
 of monad examples.   I'm trying to figure out how this is different from
 UFCS on InputRanges.   It seems like std.algorithm implements something
 which accomplished the same thing, but much easier to understand?
I think so. I could understand ranges but I still cannot understand monads! :p (That category theory thing is always in the way.)
 Can somebody maybe do a compare and contrast for me?
Me too! :)
 -Shammah
Ali
Dec 03 2013
prev sibling parent reply Max Klyga <email domain.com> writes:
On 2013-12-03 02:45:44 +0000, Shammah Chancellor said:

 I'm not particularly familiar with the syntax being used in the variet 
 of monad examples.   I'm trying to figure out how this is different 
 from UFCS on InputRanges.   It seems like std.algorithm implements 
 something which accomplished the same thing, but much easier to 
 understand?
 
 Can somebody maybe do a compare and contrast for me?
 
 -Shammah
Monads and input ranges are different things. I'll try to briefly explain monads. Hope this will not worsen the situation by being too confusing. InputRanges provide a generic way for iterating over something. UFCS can be used to create a range interface on things that do not provide it. Monads are an abstraction for composing things within some context (concatenating lists, composing operations on nullable values, composing asynchronous operations). That sounds a bit too general and vague, because it is. One can think about as a design pattern. Monad has two operations:  - make a monad out of a value  - apply a function that takes a value and returns a new monad of the same kind to value inside a monad second operation has a different meaning for different monad kinds but generally it means 'execute this code within current context' for nullable values this means 'execute only if there exist a value' for asynchronous operations this means 'execute this when the value is ready' This operation is commonly named 'bind' or 'flatMap' Some languages provide syntax sugar for monads (Scala's for, Haskell's do) Monads are easier to understand once you've seen enough examples of things that are monads. Suppose you have a list of movies and want to produce a list of names of all actors stating in those movies. In scala you would typically write something like this: for (movie <- movies; actor <- movie.actors) yield actor.name Compiler rewrites that to movies.flatMap(movie => movie.actors).map(actor => actor.name)                           ^                            ---------- this function takes a list element and returns a new list, effectively creating a list of lists and then flattening it by concatenating all the lists into one, hence the name 'flatMap'. It transforms and then flattens. Another popular example for Monads are optional values (similar to nullables but forcing you to check for presence of value and explicitly avoiding null dereferencing) A common pattern for working with optional values is returning null from your function if your input is null So if say we are parsing JSON and we want to process only values that contain certain field, that in turn contains another field. Example in pseudo-scala: for (value <- json.get("value"); // type of value is Option(JsonNode) meaning that actual json node might be absent       anotherValue <- value.get("another")) // this is executed only if value is present doSomethingFancy(anotherValue) // ditto and again, compiler will rewrite this into json.get("value").flatMap(value => value.get("another")).foreach(anotherValue => doSomethingFancy(anotherValue)) Once again we see that flat map is used. The pattern is same - get the value out of the box, transform it to another box of the same kind in the context meaningful for this particular box kind So the main benefit is being able to compose things in a consistent way. Once you grasp the whole idea its fun finding out that some thing you've been doing can be viewed as a monad. People created quite a lot of different monads to this date.
Dec 03 2013
parent reply Shammah Chancellor <anonymous coward.com> writes:
On 2013-12-03 21:51:20 +0000, Max Klyga said:

 On 2013-12-03 02:45:44 +0000, Shammah Chancellor said:
 
 I'm not particularly familiar with the syntax being used in the variet 
 of monad examples.   I'm trying to figure out how this is different 
 from UFCS on InputRanges.   It seems like std.algorithm implements 
 something which accomplished the same thing, but much easier to 
 understand?
 
 Can somebody maybe do a compare and contrast for me?
 
 -Shammah
Monads and input ranges are different things. I'll try to briefly explain monads. Hope this will not worsen the situation by being too confusing. InputRanges provide a generic way for iterating over something. UFCS can be used to create a range interface on things that do not provide it. Monads are an abstraction for composing things within some context (concatenating lists, composing operations on nullable values, composing asynchronous operations). That sounds a bit too general and vague, because it is. One can think about as a design pattern. Monad has two operations:  - make a monad out of a value  - apply a function that takes a value and returns a new monad of the same kind to value inside a monad second operation has a different meaning for different monad kinds but generally it means 'execute this code within current context' for nullable values this means 'execute only if there exist a value' for asynchronous operations this means 'execute this when the value is ready' This operation is commonly named 'bind' or 'flatMap' Some languages provide syntax sugar for monads (Scala's for, Haskell's do) Monads are easier to understand once you've seen enough examples of things that are monads. Suppose you have a list of movies and want to produce a list of names of all actors stating in those movies. In scala you would typically write something like this: for (movie <- movies; actor <- movie.actors) yield actor.name Compiler rewrites that to movies.flatMap(movie => movie.actors).map(actor => actor.name)                           ^                            ---------- this function takes a list element and returns a new list, effectively creating a list of lists and then flattening it by concatenating all the lists into one, hence the name 'flatMap'. It transforms and then flattens. Another popular example for Monads are optional values (similar to nullables but forcing you to check for presence of value and explicitly avoiding null dereferencing) A common pattern for working with optional values is returning null from your function if your input is null So if say we are parsing JSON and we want to process only values that contain certain field, that in turn contains another field. Example in pseudo-scala: for (value <- json.get("value"); // type of value is Option(JsonNode) meaning that actual json node might be absent       anotherValue <- value.get("another")) // this is executed only if value is present doSomethingFancy(anotherValue) // ditto and again, compiler will rewrite this into json.get("value").flatMap(value => value.get("another")).foreach(anotherValue => doSomethingFancy(anotherValue)) Once again we see that flat map is used. The pattern is same - get the value out of the box, transform it to another box of the same kind in the context meaningful for this particular box kind So the main benefit is being able to compose things in a consistent way. Once you grasp the whole idea its fun finding out that some thing you've been doing can be viewed as a monad. People created quite a lot of different monads to this date.
I get the gist of that, but it seems like the range concept with UFCS provides the same thing? E.G. range.map().flatten().map()? Does it really not accomplish the same thing -- am I missing some key point of monads?
Dec 03 2013
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 12/04/2013 12:02 AM, Shammah Chancellor wrote:
 I get the gist of that, but it seems like the range concept with UFCS
 provides the same thing? E.G.  range.map().flatten().map()?
Well, informally speaking, this is roughly an instance of a Monad.
 Does it really not accomplish the same thing -- am I missing some key
 point of monads?
The quick answer is that they are more general and would also capture eg. rooted trees with map and flatten operations, not just linear sequences.
Dec 03 2013
prev sibling parent reply Max Klyga <email domain.com> writes:
On 2013-12-03 23:02:13 +0000, Shammah Chancellor said:

 On 2013-12-03 21:51:20 +0000, Max Klyga said:
 
 On 2013-12-03 02:45:44 +0000, Shammah Chancellor said:
 
 I'm not particularly familiar with the syntax being used in the variet 
 of monad examples.   I'm trying to figure out how this is different 
 from UFCS on InputRanges.   It seems like std.algorithm implements 
 something which accomplished the same thing, but much easier to 
 understand?
 
 Can somebody maybe do a compare and contrast for me?
 
 -Shammah
Monads and input ranges are different things. I'll try to briefly explain monads. Hope this will not worsen the situation by being too confusing. InputRanges provide a generic way for iterating over something. UFCS can be used to create a range interface on things that do not provide it. Monads are an abstraction for composing things within some context (concatenating lists, composing operations on nullable values, composing asynchronous operations). That sounds a bit too general and vague, because it is. One can think about as a design pattern. Monad has two operations:  - make a monad out of a value  - apply a function that takes a value and returns a new monad of the same kind to value inside a monad second operation has a different meaning for different monad kinds but generally it means 'execute this code within current context' for nullable values this means 'execute only if there exist a value' for asynchronous operations this means 'execute this when the value is ready' This operation is commonly named 'bind' or 'flatMap' Some languages provide syntax sugar for monads (Scala's for, Haskell's do) Monads are easier to understand once you've seen enough examples of things that are monads. Suppose you have a list of movies and want to produce a list of names of all actors stating in those movies. In scala you would typically write something like this: for (movie <- movies; actor <- movie.actors) yield actor.name Compiler rewrites that to movies.flatMap(movie => movie.actors).map(actor => actor.name)                           ^                            ---------- this function takes a list element and returns a new list, effectively creating a list of lists and then flattening it by concatenating all the lists into one, hence the name 'flatMap'. It transforms and then flattens. Another popular example for Monads are optional values (similar to nullables but forcing you to check for presence of value and explicitly avoiding null dereferencing) A common pattern for working with optional values is returning null from your function if your input is null So if say we are parsing JSON and we want to process only values that contain certain field, that in turn contains another field. Example in pseudo-scala: for (value <- json.get("value"); // type of value is Option(JsonNode) meaning that actual json node might be absent       anotherValue <- value.get("another")) // this is executed only if value is present doSomethingFancy(anotherValue) // ditto and again, compiler will rewrite this into json.get("value").flatMap(value => value.get("another")).foreach(anotherValue => doSomethingFancy(anotherValue)) Once again we see that flat map is used. The pattern is same - get the value out of the box, transform it to another box of the same kind in the context meaningful for this particular box kind So the main benefit is being able to compose things in a consistent way. Once you grasp the whole idea its fun finding out that some thing you've been doing can be viewed as a monad. People created quite a lot of different monads to this date.
I get the gist of that, but it seems like the range concept with UFCS provides the same thing? E.G. range.map().flatten().map()? Does it really not accomplish the same thing -- am I missing some key point of monads?
You look only at the syntax side of the question. range.map(...).flatten.map(...) might look similar and it could be possible to squeeze monads to work with this api, but the thing is that not every monad could provide a meaningful map function and as a whole calling flatten after every map is a bit tiresome. That may work for some monads, like List, because its effectively a range. It will also work for Maybe monad. It could be viewed as a range of 0 or 1 elements. But things get bad when we try to define other monads in terms of range interface. Current map implementation by design doesn't know anything about range it processes. If we try to define Promise monad as a range it will practically be useless unless we provide a custom map implementation for promises, because std.algorithm.map will return a wrapper range that will call popFront() that will block and wait for the value but that defeats the purpose entirely as we wanted the result to be mapped asynchronously when it will be available and not block. What about other monads? Defining IO or State monads as a range would be just impossible So input range can be viewed as a monad but not the other way around. Each monad needs its own unique flatMap implementation
Dec 03 2013
next sibling parent reply Shammah Chancellor <anonymous coward.com> writes:
On 2013-12-03 23:49:47 +0000, Max Klyga said:

 On 2013-12-03 23:02:13 +0000, Shammah Chancellor said:
 
 On 2013-12-03 21:51:20 +0000, Max Klyga said:
 
 On 2013-12-03 02:45:44 +0000, Shammah Chancellor said:
 
 I'm not particularly familiar with the syntax being used in the variet 
 of monad examples.   I'm trying to figure out how this is different 
 from UFCS on InputRanges.   It seems like std.algorithm implements 
 something which accomplished the same thing, but much easier to 
 understand?
 
 Can somebody maybe do a compare and contrast for me?
 
 -Shammah
Monads and input ranges are different things. I'll try to briefly explain monads. Hope this will not worsen the situation by being too confusing. InputRanges provide a generic way for iterating over something. UFCS can be used to create a range interface on things that do not provide it. Monads are an abstraction for composing things within some context (concatenating lists, composing operations on nullable values, composing asynchronous operations). That sounds a bit too general and vague, because it is. One can think about as a design pattern. Monad has two operations:  - make a monad out of a value  - apply a function that takes a value and returns a new monad of the same kind to value inside a monad second operation has a different meaning for different monad kinds but generally it means 'execute this code within current context' for nullable values this means 'execute only if there exist a value' for asynchronous operations this means 'execute this when the value is ready' This operation is commonly named 'bind' or 'flatMap' Some languages provide syntax sugar for monads (Scala's for, Haskell's do) Monads are easier to understand once you've seen enough examples of things that are monads. Suppose you have a list of movies and want to produce a list of names of all actors stating in those movies. In scala you would typically write something like this: for (movie <- movies; actor <- movie.actors) yield actor.name Compiler rewrites that to movies.flatMap(movie => movie.actors).map(actor => actor.name)                           ^                            ---------- this function takes a list element and returns a new list, effectively creating a list of lists and then flattening it by concatenating all the lists into one, hence the name 'flatMap'. It transforms and then flattens. Another popular example for Monads are optional values (similar to nullables but forcing you to check for presence of value and explicitly avoiding null dereferencing) A common pattern for working with optional values is returning null from your function if your input is null So if say we are parsing JSON and we want to process only values that contain certain field, that in turn contains another field. Example in pseudo-scala: for (value <- json.get("value"); // type of value is Option(JsonNode) meaning that actual json node might be absent       anotherValue <- value.get("another")) // this is executed only if value is present doSomethingFancy(anotherValue) // ditto and again, compiler will rewrite this into json.get("value").flatMap(value => value.get("another")).foreach(anotherValue => doSomethingFancy(anotherValue)) Once again we see that flat map is used. The pattern is same - get the value out of the box, transform it to another box of the same kind in the context meaningful for this particular box kind So the main benefit is being able to compose things in a consistent way. Once you grasp the whole idea its fun finding out that some thing you've been doing can be viewed as a monad. People created quite a lot of different monads to this date.
I get the gist of that, but it seems like the range concept with UFCS provides the same thing? E.G. range.map().flatten().map()? Does it really not accomplish the same thing -- am I missing some key point of monads?
You look only at the syntax side of the question. range.map(...).flatten.map(...) might look similar and it could be possible to squeeze monads to work with this api, but the thing is that not every monad could provide a meaningful map function and as a whole calling flatten after every map is a bit tiresome. That may work for some monads, like List, because its effectively a range. It will also work for Maybe monad. It could be viewed as a range of 0 or 1 elements. But things get bad when we try to define other monads in terms of range interface. Current map implementation by design doesn't know anything about range it processes. If we try to define Promise monad as a range it will practically be useless unless we provide a custom map implementation for promises, because std.algorithm.map will return a wrapper range that will call popFront() that will block and wait for the value but that defeats the purpose entirely as we wanted the result to be mapped asynchronously when it will be available and not block. What about other monads? Defining IO or State monads as a range would be just impossible So input range can be viewed as a monad but not the other way around. Each monad needs its own unique flatMap implementation
But then, these other monads could be defined in D using UFCS? Or is D syntax not generic enough to define monads?
Dec 03 2013
next sibling parent Max Klyga <email domain.com> writes:
On 2013-12-04 01:53:39 +0000, Shammah Chancellor said:

 On 2013-12-03 23:49:47 +0000, Max Klyga said:
 
 On 2013-12-03 23:02:13 +0000, Shammah Chancellor said:
 
 On 2013-12-03 21:51:20 +0000, Max Klyga said:
 
 On 2013-12-03 02:45:44 +0000, Shammah Chancellor said:
 
 I'm not particularly familiar with the syntax being used in the variet 
 of monad examples.   I'm trying to figure out how this is different 
 from UFCS on InputRanges.   It seems like std.algorithm implements 
 something which accomplished the same thing, but much easier to 
 understand?
 
 Can somebody maybe do a compare and contrast for me?
 
 -Shammah
Monads and input ranges are different things. I'll try to briefly explain monads. Hope this will not worsen the situation by being too confusing. InputRanges provide a generic way for iterating over something. UFCS can be used to create a range interface on things that do not provide it. Monads are an abstraction for composing things within some context (concatenating lists, composing operations on nullable values, composing asynchronous operations). That sounds a bit too general and vague, because it is. One can think about as a design pattern. Monad has two operations:  - make a monad out of a value  - apply a function that takes a value and returns a new monad of the same kind to value inside a monad second operation has a different meaning for different monad kinds but generally it means 'execute this code within current context' for nullable values this means 'execute only if there exist a value' for asynchronous operations this means 'execute this when the value is ready' This operation is commonly named 'bind' or 'flatMap' Some languages provide syntax sugar for monads (Scala's for, Haskell's do) Monads are easier to understand once you've seen enough examples of things that are monads. Suppose you have a list of movies and want to produce a list of names of all actors stating in those movies. In scala you would typically write something like this: for (movie <- movies; actor <- movie.actors) yield actor.name Compiler rewrites that to movies.flatMap(movie => movie.actors).map(actor => actor.name)                           ^                            ---------- this function takes a list element and returns a new list, effectively creating a list of lists and then flattening it by concatenating all the lists into one, hence the name 'flatMap'. It transforms and then flattens. Another popular example for Monads are optional values (similar to nullables but forcing you to check for presence of value and explicitly avoiding null dereferencing) A common pattern for working with optional values is returning null from your function if your input is null So if say we are parsing JSON and we want to process only values that contain certain field, that in turn contains another field. Example in pseudo-scala: for (value <- json.get("value"); // type of value is Option(JsonNode) meaning that actual json node might be absent       anotherValue <- value.get("another")) // this is executed only if value is present doSomethingFancy(anotherValue) // ditto and again, compiler will rewrite this into json.get("value").flatMap(value => value.get("another")).foreach(anotherValue => doSomethingFancy(anotherValue)) Once again we see that flat map is used. The pattern is same - get the value out of the box, transform it to another box of the same kind in the context meaningful for this particular box kind So the main benefit is being able to compose things in a consistent way. Once you grasp the whole idea its fun finding out that some thing you've been doing can be viewed as a monad. People created quite a lot of different monads to this date.
I get the gist of that, but it seems like the range concept with UFCS provides the same thing? E.G. range.map().flatten().map()? Does it really not accomplish the same thing -- am I missing some key point of monads?
You look only at the syntax side of the question. range.map(...).flatten.map(...) might look similar and it could be possible to squeeze monads to work with this api, but the thing is that not every monad could provide a meaningful map function and as a whole calling flatten after every map is a bit tiresome. That may work for some monads, like List, because its effectively a range. It will also work for Maybe monad. It could be viewed as a range of 0 or 1 elements. But things get bad when we try to define other monads in terms of range interface. Current map implementation by design doesn't know anything about range it processes. If we try to define Promise monad as a range it will practically be useless unless we provide a custom map implementation for promises, because std.algorithm.map will return a wrapper range that will call popFront() that will block and wait for the value but that defeats the purpose entirely as we wanted the result to be mapped asynchronously when it will be available and not block. What about other monads? Defining IO or State monads as a range would be just impossible So input range can be viewed as a monad but not the other way around. Each monad needs its own unique flatMap implementation
But then, these other monads could be defined in D using UFCS? Or is D syntax not generic enough to define monads?
Yes, they could be defined with or without UFCS, it may just be a little inconvinient to use without syntax sugar. Monads could be defined in any language that has first class functions
Dec 03 2013
prev sibling parent reply "qznc" <qznc web.de> writes:
On Wednesday, 4 December 2013 at 01:53:39 UTC, Shammah Chancellor 
wrote:
 Or is D syntax not generic enough to define monads?
I started to port monads to D [0]. You can do it, but it looks ugly. The trick is to implement (Haskell) type classes via template specialization. I came to the conclusion that it is not worth it. What D kind of lacks is a way to define a general type class aka the interface. Of course, you could use the "interface" keyword, but then you cannot apply it to structs. Haskell has no structs (value type records), so they do not have this problem. Look at how isInputRange is implemented [1]. The traits in Rust [2] provide this interface mechanisms as a language feature. D uses static-if instead. Not Haskell, not D, not Rust can check, if your monad actually follows the monad laws [3]. This would probably require a full theorem prover in your language. So Coq, Isabelle, and maybe ATS could do that. A similar challenge would be to check if a user-defined plus operator is commutative (a+b == b+a) like arithmetic plus operations. [0] https://bitbucket.org/qznc/d-monad/src/5b9d41c611093db74485b017a72473447f8d5595/generic.d?at=master [1] https://github.com/D-Programming-Language/phobos/blob/master/std/range.d#L519 [2] http://static.rust-lang.org/doc/0.8/tutorial.html#generics [3] http://www.haskell.org/haskellwiki/Monad_laws
Dec 04 2013
next sibling parent reply Shammah Chancellor <anonymous coward.com> writes:
On 2013-12-04 08:24:02 +0000, qznc said:

 On Wednesday, 4 December 2013 at 01:53:39 UTC, Shammah Chancellor wrote:
 Or is D syntax not generic enough to define monads?
I started to port monads to D [0]. You can do it, but it looks ugly. The trick is to implement (Haskell) type classes via template specialization. I came to the conclusion that it is not worth it. What D kind of lacks is a way to define a general type class aka the interface. Of course, you could use the "interface" keyword, but then you cannot apply it to structs. Haskell has no structs (value type records), so they do not have this problem. Look at how isInputRange is implemented [1]. The traits in Rust [2] provide this interface mechanisms as a language feature. D uses static-if instead. Not Haskell, not D, not Rust can check, if your monad actually follows the monad laws [3]. This would probably require a full theorem prover in your language. So Coq, Isabelle, and maybe ATS could do that. A similar challenge would be to check if a user-defined plus operator is commutative (a+b == b+a) like arithmetic plus operations. [0] https://bitbucket.org/qznc/d-monad/src/5b9d41c611093db74485b017a72473447f8d5595 generic.d?at=master [1] https://github.com/D-Programming-Language/phobos/blob/master/std/range.d#L519 [2] http://static.rust-lang.org/doc/0.8/tutorial.html#generics [3] http://www.haskell.org/haskellwiki/Monad_laws
I was talking on the D newsgroup about using an interface type and implementing isConceptOf!(<interface>) to be used with structs. Wouldn't that fix the problem? -Shammah
Dec 04 2013
parent "Dicebot" <public dicebot.lv> writes:
On Wednesday, 4 December 2013 at 12:08:32 UTC, Shammah Chancellor 
wrote:
 I was talking on the D newsgroup about using an interface type 
 and implementing isConceptOf!(<interface>) to be used with 
 structs.    Wouldn't that fix the problem?

 -Shammah
No. D does not check if code of template actually conforms its constraint. You can still call functions that are not guaranteed to be there by constraint thus it is not really a type class / concept, just syntax convenience for constraint.
Dec 04 2013
prev sibling parent "Max Klyga" <max.klyga gmail.com> writes:
On Wednesday, 4 December 2013 at 08:24:03 UTC, qznc wrote:
 On Wednesday, 4 December 2013 at 01:53:39 UTC, Shammah 
 Chancellor wrote:
 Or is D syntax not generic enough to define monads?
I started to port monads to D [0]. You can do it, but it looks ugly. The trick is to implement (Haskell) type classes via template specialization. I came to the conclusion that it is not worth it. What D kind of lacks is a way to define a general type class aka the interface. Of course, you could use the "interface" keyword, but then you cannot apply it to structs. Haskell has no structs (value type records), so they do not have this problem. Look at how isInputRange is implemented [1]. The traits in Rust [2] provide this interface mechanisms as a language feature. D uses static-if instead.
D uses static if and template constraints for typeclass/concept checking because one cannot add specializations to templates defined in other modules. Using template specialization for defining type class instances would render them not extensible for users
Dec 04 2013
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 12/04/2013 12:49 AM, Max Klyga wrote:
 range.map(...).flatten.map(...) might look similar and it could be
 possible to squeeze monads to work with this api,  but the thing is that
 not every monad could provide a meaningful map function
Yes, every monad provides a meaningful way to map morphisms. In Haskell this is not explicit however: map :: Monad m => (a -> b) -> m a -> m b map f = (return . f =<<)
 and as a whole
 calling flatten after every map is a bit tiresome.
Dec 04 2013
parent "Max Klyga" <max.klyga gmail.com> writes:
On Wednesday, 4 December 2013 at 10:03:51 UTC, Timon Gehr wrote:
 On 12/04/2013 12:49 AM, Max Klyga wrote:
 range.map(...).flatten.map(...) might look similar and it 
 could be
 possible to squeeze monads to work with this api,  but the 
 thing is that
 not every monad could provide a meaningful map function
Yes, every monad provides a meaningful way to map morphisms. In Haskell this is not explicit however: map :: Monad m => (a -> b) -> m a -> m b map f = (return . f =<<)
 and as a whole
 calling flatten after every map is a bit tiresome.
You are right. Now looking at provided implementation this seems obvoius
Dec 04 2013