Skip to content

Instantly share code, notes, and snippets.

@reiddraper
Created January 14, 2013 22:24

Revisions

  1. reiddraper created this gist Jan 14, 2013.
    304 changes: 304 additions & 0 deletions erlang-monads.markdown
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,304 @@
    !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?]