Functors,
Applicatives,
Monads
...WAT?

dev* 2014

@jyrimatti

motivation

  • monads are so last millenium.
    We should already know what they are
  • it's useful to know and understand abstractions,
    even those we don't directly need in our everyday life
  • I'll buy you a beer if you find this presentation useful ;)

disclaimer

In addition to it begin useful, it is also cursed and the curse of the monad is that once you get the epiphany, once you understand - "oh that's what it is" - you lose the ability to explain it to anybody.
-- Douglas Crockford

spoiler

it's not a burrito

http://www.carlosandgabbys.com/chicken%20burrito.jpg http://www.carlosandgabbys.com/chicken%20burrito.jpg

a function

In mathematics, a function is a relation between a set of inputs and a set of permissible outputs with the property that each input is related to exactly one output.
http://en.wikipedia.org/wiki/Function_(mathematics)

						f :: a -> b
					

familiar with Haskell syntax?

function composition

In mathematics, function composition is the pointwise application of one function to the result of another to produce a third function.
http://en.wikipedia.org/wiki/Function_composition

compose :: (b -> c) -> (a -> b) -> (a -> c)
compose g f = \a -> g (f a)
            = g . f
					
  • "chaining" functions together
  • notice how this enforces an order for the function applications

function application

in mathematics, function application is the act of applying a function to an argument from its domain so as to obtain the corresponding value from its range.
http://en.wikipedia.org/wiki/Function_application

apply :: (a -> b) -> a -> b
					

what if our values are "boxed", inside:

  • an Option/Optional/Maybe?
  • a Wicket IModel?
  • a JavaScript Promise?
  • a List?
  • ...

noWay :: Bool -> Bool
someValue :: Maybe Bool

noWay someValue -- whoops, won't compile
					

a functor

In mathematics, a functor is a type of mapping between categories, which is applied in category theory.
http://en.wikipedia.org/wiki/Functor

just forget that.

  • "something that can be mapped over"
  • a boxed value
  • a value with a context

class Functor f where
  fmap :: (a -> b) -> f a -> f b
					
now this works:

noWay :: Bool -> Bool
someValue :: Maybe Bool

(fmap noWay) someValue -- hooray!
						
  • generalizes function application to work with boxed values
  • you probably shouldn't think of them as boxes.
    or maybe you should.
    I don't know.
  • there's so little structure that the abstraction stays simple

what if our function takes more parameters?


orElse :: Bool -> Bool -> Bool

(fmap orElse) :: Maybe Bool -> Maybe (Bool -> Bool) -- whoops...
					

return value wrapped inside the container,
need something capable of handling this

an applicative


class Applicative f where
  pure :: a -> f a
  (<*>) :: f (a -> b) -> f a -> f b
					

notice how the signature is just what we needed

now this works:

orElse :: Bool -> Bool -> Bool
someValue :: Maybe Bool

(pure orElse) <*> someValue <*> someValue -- hooray!
						
  • b can be a function, so this can be applied multiple times
  • does not say anything about the order of execution,
    so inherently parallelizable
  • generalizes boxed function application to work
    with functions of arity > 1

what if we want to interact with the box?

that is, affect the execution based on computed values?

that is, determine execution based on context?

a monad


class Monad m where
  return :: a -> m a
  (>>=) :: m a -> (a -> m b) -> m b
					

we have a wrapped value, and we add "actions" to it one by one

(return 1) >>= (\one -> return (one + 1)) >>= (\two -> return (two + 1)) -- and so on...

notice how this enforces the order of execution

intuition?

we can change the order of arguments:

(>>=) :: m a -> (a -> m b) -> m b
(=<<) :: (a -> m b) -> m a -> m b

now the first part (the initial monadic value) can be given last:

(\one -> return (one + 1) >>= (\two -> return (two + 1))) =<< (return 1)

simplify syntax:

(a -> m b >>= (b -> m c)) =<< m a

compare to function composition,
whose arguments may as well be interchanged:

(.) :: (b -> c) -> (a -> b) -> (a -> c)
(>>>) :: (a -> b) -> (b -> c) -> (a -> c)
(a -> b) >>> (b -> c) $ a

monad generalizes function composition
to work with functions returning boxed values

lifting

Lifting is a concept which allows you to transform a function into a corresponding function within another (usually more general) setting.
https://www.haskell.org/haskellwiki/Lifting

functor/applicative/monad can be thought of as
an alternative way to apply functions


apply :: (a -> b) -> a -> b
fmap  :: (a -> b) -> f a -> f b
(<*>) :: f (a -> b) -> f a -> f b
(=<<) :: (a -> m b) -> m a -> m b
							

or a function can be thought to be lifted to another context


fmap   :: (a -> b) -> (f a -> f b)
liftA2 :: (a -> b -> c) -> (f a -> f b -> f c)  -- and liftA, liftA3, ...
liftM2 :: (a -> b -> c) -> (m a -> m b -> m c)  -- and liftM, liftM3, ...
							

syntax

  • binding actions to a Monad one after another while retaining all intermediate variables in scope, is a pattern also known as callback hell
  • using a common abstraction throughout a language lets us add some syntactic sugar to ease the pain
  • in Haskell that sugar is the do-notation, which transforms the callback-tree to an imperative-looking list of actions


anyway, it's just syntax

laws

all these concepts must satisfy certain laws to ensure they are well behaved

we will not go there now =)

Monads and IO in Haskell?

Monads have nothing to do with IO

...but IO needs the order of execution, which happens to be just the concept that a monad can provide

See https://www.haskell.org/haskellwiki/IO_inside for more info

going further


class Functor f where
  fmap :: (a -> b) -> f a -> f b
					

class Applicative f where
  pure :: a -> f a
  (<*>) :: f (a -> b) -> f a -> f b
					

class Monad m where
  return :: a -> m a
  (>>=) :: m a -> (a -> m b) -> m b -- or: =<< :: (a -> m b) -> m a -> m b
					

apply ::   (a -> b) -> a -> b
fmap  ::   (a -> b) -> f a -> f b
(<*>) :: f (a -> b) -> f a -> f b
=<<   :: (a -> m b) -> m a -> m b
					

anything missing?


??    :: (w a -> b) -> w a -> w b
					
  • YES, there is!
  • it's called a comonad
  • it about push (monad) versus pull (comonad)
  • not explaining here what it is since I still lack the intuition

there's still A LOT more to learn.


go learn!


thank you.