Skip to content
Robert Peszek edited this page Sep 22, 2013 · 1 revision

Monads

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.

References

Defining Monads in Fpiglet

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).

Monad Laws:

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.

Monads defined within Fpiglet:

###Monad Function Library: See MonadFunctions.

###Idiomatic Use of Monads:

###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.
Clone this wiki locally