-
Notifications
You must be signed in to change notification settings - Fork 3
MonadPolymorphism
If you are new to monads and find the concept hard to understand, please read my introductory blog post linked in the references. I believe monads are not hard at all and I have done a good job in my blog explaining how to think about them.
-
I have created an introductory blog for Groovy developers about what monads are: http://rpeszek.blogspot.com/2013/07/coding-with-monads-in-groovy-more.html
-
Very good writeup on monads in Haskell wiki: http://en.wikibooks.org/wiki/Haskell/Understanding_monads
-
Wikipedia: http://en.wikipedia.org/wiki/Monad_(functional_programming)
Monads are defined by extending abstract class:
fpig.concepts.MonadDescription
This class requires implementing 2 closures:
pure - places objects of mapped type in 'simplest' context
logical signature:
pure:: a -> m a
M pure(T t)
bind - transforms Closures so they take monadic input parameters
logical signature:
bind:: (a -> m b) -> m a -> m b
Closure<M> bind Closure<T>
NOTE: Monad class Mx and MxMonadDescription should be two different classes.
For example, Fpiglet Either
is a class and EitherMonad
is a separate
class extending MonadDescription.
This is because bind
and pure
should not be 'owned' by Either and that idea is similar to filter
, map
not belonging to the 'List'. This is not OO programming. Nouns are not more important than verbs.
NOTE 2: if you are comparing MonadDescription to Haskell Monad type class, the bind
function is flipped:
//logical signature in Fpiglet
bind:: (a -> mb) -> ma -> mb
//Haskell signature
>>= :: m a -> (a -> m b) -> m b
because of that in Haskell, monadic composition chains go from left to right (>>= is infix).
I like the fact that, in Fpiglet, the direction of monadic composition is the same as function composition.
Also Fpilget uses the term 'pure' instead of Haskell's 'return'.
The term 'unit' is also used in FP, Fpilget settled on pure
for consistency with Applicative (ApplicativePolymorphism).
You can choose to define a stronger version of monad (one defining emptyM concept) by extending:
fpig.concepts.MonadWithEmptyDescription
such monads will work better with MonadicComprehensions. In FP such monad is often called MonadZero
.
Once the MonadDescription is implemented you can do monadic computing using your new class or use it with comprehensions.
New monads can optionally be registered with Fpiglet using
FpigMonads.configureMonad(monadClass, monadDescriptionInstance)
Registering custom monads makes them available for use with polymorphic functions defined in the FpigBase
module. These functions work across all registered monads (for example, FpigBase.bind
, FpigBase.unpure
, FpigBase.mfilter
).
Converting to Applicative or to Functor:
MonadDescription
can be converted to a FunctorDescription
(FunctorPolymorphism) or ApplicativeDescription
(ApplicativePolymorphism) by wrapping it in MonadAsApplicative
class:
def myFuctor = new MonadAsApplicative(myMonad)
Note: it is more straightforward and will be more performant if Functor is defined explicitly. Derived fmap
may not be as fast.
Another Note: Fpiglet monad fuctions (fpig.monad.functions.BaseM
) includes the ap
function and most of functions defined in for Applicative (fpig.applicative.functions.BaseA
).
To be a 'real' monad the bind and pure definition cannot be arbitrary. They needs to follow these rules:
bind(fn, pure(x)) '==' fn(x) //(or b(f) << pure(x) '==' f(x)) (Left identity)
bind(pure, mx) '==' mx //(or b(pure) << mx '==' mx) (Right identity)
bind(fn, bind(g, mx)) '==' bind({x -> (bind(fn, g(x))}, mx) //or b(fn) << b(g) << mx '==' b({x -> (b(fn) << g) (x)}) (mx) (Associativity)
If you are new to monads, you may need to stare at these laws a bit before understanding them. They are not as complicated as they look. Fpiglet has a test case which verifies them for some Monads.
- Non-deterministic calculation FunListMonad
- Maybe is a monad
- Either is a monad
- Function composition ClosureMonad
###Monad Function Library: See MonadFunctions.
###Idiomatic Use of Monads:
- Composition: MonadicComposition
- Comprehension: MonadicComprehensions
###Why I call it Monad Polymorphism? All combinator libraries are very polymorphic. But here is a short list of more explicit reasons:
- we can write functions which work across all monads (MonadFunctions).
- comprehension syntax MonadicComprehensions applies to all monads.
- Monads are stronger version of Functor and Applicative. They 'lift' functionality developed for the type they map. For example, FunList is a monad, so operations that work on integers such as '+' or 'MAX' can be lifted and applied to lists of integers using
app2
monadic function.