Reputation: 2312
The language suggestion states that the advantages are detailed in the linked paper. I had a quick skim through and I can't see it spelled out explicitly.
Is the advantage just that each statement gets executed in parallel so I might get a speed boost?
Or is there some kind of logic it caters for, that is not convenient using the usual monadic let!
?
I understand that, this being applicative, means that it comes with the limitation that I can't use previous expressions to determine the logic of subsequent expressions. So does that mean the trade off is flexibility for efficiency?
Upvotes: 5
Views: 140
Reputation: 11577
I am excited for this for two reasons. Potentially better performance and that some things that can't fit into the Monad pattern can fit into the Applicative Functor pattern.
In order to support the already existing let!
one need to implement Bind
and Return
, ie the Monad pattern.
let!
and and!
requires Apply
and Pure
(not exactly sure what F# the equivalent are named), ie the Applicative Functor pattern.
Applicative Functors are less powerful than Monads but still very useful. For example, in parsers.
As mentioned by Philip Carter, Applicative Functor can be made more efficient than Monads as it's possible to cache them.
The reason is the signature of Bind
: M<'T> -> ('T -> M<'U>) -> M<'U>
.
One typical way you implement bind is that you "execute" the first argument to produce a value of 'T
and pass it the second argument to get the next step M<'U>
which you execute as well.
As the second argument is a function that can return any M<'U>
it means caching is not possible.
With Apply
the signature is: M<'T -> 'U> -> M<'T> -> M<'U>
. As neither of the two inputs are functions you can potentially cache them or do precomputation.
This is useful for parsers, for example in FParsec
for performance reasons it's not recommended to use Bind
.
In addition; a "drawback" with Bind
is that if the first input doesn't produce a value 'T
there is no way to get the second computation M<'U>
. If you are building some kind of validator monad this means that as soon as a valid value can't be produced because the input is invalid then the validation will stop and you won't get a report of the remaining input.
With Applicative Functor you can execute all validators to produce a validation report for the entire document.
So I think it's pretty cool!
Upvotes: 4
Reputation: 2459
The way I understood Don Syme's description, when I read it a while ago, was every step in the let! ... and! ...
chain will be executed, unlike when you use let! ... let! ...
. Say, for instance, that you use the option
CE. Then if you write
option {
let! a = parseInt("a")
let! b = parseInt("b")
return a + b
}
only the first let!
will be executed, as the CE short-circuits as soon as it meets a None. Writing instead let! a ... and! b = ...
will try to parse both strings; though not necessarily in parallel, as I understand it.
I see a huge benefit in this, when parsing data from external sources. Consider for instance running
parse {
let! name = tryParse<Name>("John Doe")
and! email = tryParse<EMail>("johndoe@")
and! age = tryParse<uint>("abc")
return (name, email, age)
}
and getting an Error(["'johndoe@' isn't a valid e-mail"; "'abc' isn't a valid uint"])
in return, instead of only the first of the errors. That's pretty neat to me.
Upvotes: 6