Monads compared to InputRanges?

Shammah Chancellor anonymous at coward.com
Tue Dec 3 17:53:39 PST 2013


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?



More information about the Digitalmars-d-learn mailing list