Monads and the like

Monads and the like

What I understand about it now

(Renarks / additions welcome.)

Laws

In the following S is a set with an associated binary operation O.
Some "synonyms" may concern the same operation, but belong to different levels. Ap in this way is the Monad-version of <*> (Applicative Functor-version).

The benefits of Monads for the programmer

I'm just talking about Monads here. It will be clear that in some cases it is sufficient for instance to be a Functor.


I. Semigroup
Examples
Constructor: *
Set of whole numbers with + or * operation: (4 + 10) + 2 = 4 + (10 +2)
Examples without identity: positive whole numbers with + operator, or not-empty sequences.
Laws
Closure and Associativity
Operation
append

II. Monoid
Examples
Set of whole numbers with + or * operation: a+0=a, a*1=a
Lists, Bool, Ordering, Maybe, selfdefined Tree.
Collection with operation, which you (recursively) can expand.
Laws Like Semigroup, with Identity
  • mempty `mappend` x = x
  • x `mappend` mempty = x
  • (x `mappend` y) `mappend` z = x `mappend` (y `mappend` z)
mempty represents the identity-element for a certain monoid with operation, mappend is the binairy function.
mconcat is a secundairy function that reduces a list of monoids by a fold-operation to a single value.
(mconcat is an unfortunate name, in an addition for instance nothing is joined together.)
Operations
Like Semigroep, with the zero-operation added.



III. Functor
Examples
Unary type constructor:  *->*
Binary type constructor: *->*->*
Functor[F[_]]

Scala: List, Option, Set, Either, Try, Future...
Haskell: List, Maybe, eigen Tree, Either, Function ...
"Everything that can be mapped over", applying a function to one or more values in a context.
Mapping over a function is function-composition (Nicholas, see also LYAH Functor)
Laws Identity and Compose
1. Indentity
fmap id = id
2. Compose
fmap (f . g) = fmap f . fmap g , or
fmap (f . g) F = fmap f (fmap g F)
Operation One typeclass-method: fmap :: (a -> b) -> f a -> f b
(map is the List-implementation of fmap)
Map, Either, Function1 are binary, for which the operation must be applied partially, in steps.

IV. Applicative (-Functor)
Examples The applied function itself can also be wrapped in a context
Laws
  • pure f <*> x == fmap f x
  • pure id <*> v == v
  • pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
  • pure f <*> pure x = pure (f x)
  • u <*> pure y = pure ($ y) <*> u
Operations Like Functor, with Pure added (Wraps a value in an applicative),
and ap or <*>(Takes an applicative containing a function and an other applicative, extracts the function(s) from the first and maps it over the second).
Applicatives are composable, which is not the case for every Monad.

V. Monad
Examples
Higher order type operator: (*->*)->*
Monad[M[_]]

M[_] is an unary type-constructor like List or Option
Scala: List, Option, Set, Either, Try, Future...
Haskell: List, Maybe, Tree?, Either, ...
"Informaly a Monad is everything with a constructor and a flatMap (or bind) method."
Laws
Left identity
return x >>= f is the same as f x
Right identity
m >>= return is the same as m
Associativity
The result of (m >>= f) >>= g is the same as that of m >>= (\x -> f x >>= g)
Operations

class Applicative m => Monad m where
    return :: a -> m a
    (>>=)  :: m a -> (a -> m b) -> m b
 
    (>>)   :: m a -> m b -> m b
    fail   :: String -> m a
Like Applicative, with bind, >>= or do-notation. (In Scala: flatMap.)

Next to bind, return takes care of the wrapping of a value in the monad (a "lift"-function, like pure of the applicative), and >> ("then") takes care of the chaining of monadic actions that don't need the result of the predecessor. fail is just needed internally for pattern-match failures in do-notation.

Just like the Applicative (-Functor), Arrow also is a superclass of Monad, but it doesn't fit well in this scheme. Arrows miss an other law and have a different application-area (i.a. parsers).

Copyright © 2020 Hans de Jong, all rights reserved