Monads compared to InputRanges?
Max Klyga
email at domain.com
Tue Dec 3 23:13:34 PST 2013
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
More information about the Digitalmars-d-learn
mailing list