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