Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?

!SLIDE

Monads in Erlang

monads are simple

!SLIDE

a monad is a monoid in the category of endofunctors

!SLIDE

problem?

!SLIDE

monads get a bad wrap from monad tutorials

!SLIDE

a monad is a Burrito

!SLIDE

a computation container

!SLIDE

category theory

!SLIDE

i'll give a concrete definition, in Erlang

!SLIDE

@@@ erlang
-type monad() :: term().

!SLIDE

@@@ erlang
-type list(Something).

!SLIDE

@@@ erlang
-spec return(term()) -> monad().

!SLIDE

@@@ erlang
-spec bind(monad(), fun((term()) -> monad())) -> monad().

!SLIDE

@@@ erlang
-type either() :: {ok, term()} | {error, term()}.

!SLIDE

@@@ erlang
-spec return(term()) -> either().
return(Value) ->
    {ok, Value}.

!SLIDE

@@@ erlang
-spec bind(monad(), fun((term()) -> either())) -> either().
bind({ok, Val}, MonadicFun) ->
    MonadicFun(Val);
bind({error, _Reason}=Error, _MonadicFun) ->
    Error.

!SLIDE

motivation

!SLIDE

@@@ erlang
doThatThing() ->
    case partOne() of
        {ok, Result1} ->
            case partTwo(Result1) of
                {ok, Result2} ->
                    {ok, 'wow_you_followed_this_far?'};
                {error, _Reason2}=Reason2 ->
                    Reason2
            end;
        {error, _Reason1}=Reason1 ->
            Reason1
    end.

!SLIDE

what we really want to say

@@@ erlang
[fun partOne/0, fun partTwo/1].

!SLIDE

@@@ erlang
bind(partOne(), fun partTwo/1).

!SLIDE

we can chain this together

!SLIDE

@@@ erlang
pipe(Monad, Funs) ->
    lists:foldl(flip(fun bind/2), Monad, Funs).

!SLIDE

@@@ erlang
mAddOne(Val) ->
    Val + 1.

mAlwaysError(Val) ->
    {error, Val}.

!SLIDE

@@@ erlang
pipe({ok, 0}, [fun mAddOne/1, fun mAddOne/1, mAddOne/1, fun mAlwaysError/1]).

!SLIDE

polymorphism

!SLIDE

fucking erlang

!SLIDE

modules

!SLIDE

@@@ erlang
ModuleVar:bind(...)
ModuleVar:return(...)

!SLIDE

other monads

!SLIDE

writer monad

!SLIDE

@@@ erlang
return(Val) ->
    {Val, []}.

writer_bind({Val, SideVal}, MonadFun) ->
    {NewVal, NewSideVal} = MonadFun(Val),
    {NewVal, SideVal ++ NewSideVal}.

!SLIDE

questions?


A brief digression, function composition

!SLIDE

Result = g(f(X)).

NOTE: we can reason about this

!SLIDE

Composition is associative

(g ∘ f)(x) = g(f(x)) 

!SLIDE

Let's write comp in Erlang

comp(A, B) ->
    fun(Input) ->
        A(B(Input))
    end.

!SLIDE

Now let's use it,

(comp(fun addOne/1, fun multiplyTwo/1))(5) %% -> 11

!SLIDE

Functions aren't just abstractions, we've generalized them.

!SLIDE

Sometimes we have a domain-specific idea of how to compose data or computation. This is a monad.

!SLIDE

Return the first 'failure', otherwise keep going

!SLIDE

[fun thingOne/1,
 fun thingTwo/1,
 fun thingThree/1].

!SLIDE

But here's what we do

doThatThing() ->
    case partOne() of
        {ok, Result1} ->
            case partTwo(Result1) of
                {ok, Result2} ->
                    {ok, 'wow_you_followed_this_far?'};
                {error, _Reason2}=Reason2 ->
                    Reason2
            end;
        {error, _Reason1}=Reason1 ->
            Reason1
    end.

!SLIDE

In programming, a concrete monad implements the monad 'interface'

bind
return

!SLIDE

bind defines how your data or compuation composes.

!SLIDE

return takes a non-monadic value and places it inside the monad

!SLIDE

For our failure monad, we might define bind like this:

!SLIDE

bind({ok, X}, NextFun) ->
    NextFun(X);
bind({error, _Reason}=Reason, _NextFun) ->
    Reason.

!SLIDE

return(Val) ->
    {ok, Val}.

!SLIDE

There are actually two other monad functions, but they're not as important:

>>
fail

[Note: writer monad for timings?]

@zkessin

This comment has been minimized.

Copy link

commented Jan 15, 2013

Is there a video of the talk anywhere?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.