Tests

do

This package brings monads, applicatives, functors, and do-notation to Erlang. Inspired by Haskell's type system, it provides behaviours for a range of useful type classes, as well as implementations for commonly used class instances.

Installation

To install the latest release from hex, add do to the deps in your rebar config file:

{do, "1.10.0"}

What's in the box

The do package provides implementations for either, list and maybe monads, as well as a range of useful functors. See do_types.hrl for type definitions, and hex docs for a complete overview of functions and types.

The fmap macro

The ?fmap macro can be used to map functions over functors:

-include_lib("do/include/do.hrl").
add1(N) -> N + 1.
fmap_example() ->
{ok, 2} = ?fmap(fun add1/1, {ok, 1}),
{error, reason} = ?fmap(fun add1/1, {error, reason}),
{just, 2} = ?fmap(fun add1/1, {just, 1}),
nothing = ?fmap(fun add1/1, nothing),
[2, 3, 4] = ?fmap(fun add1/1, [1, 2, 3]),
#{a => 2} = ?fmap(fun add1/1, #{a => 1}).

The bind macro

The ?bind macro can be used to bind (>>=) a function that returns a monad to a monad of the same type. For example (with either monad):

-include_lib("do/include/do.hrl").
maybe_add1(1) -> {ok, 2};
maybe_add1(_) -> {error, not_1}.
bind_example() ->
{ok, 2} = ?bind({ok, 1}, fun maybe_add1/1),
{error, not_1} = ?bind({ok, 2}, fun maybe_add1/1).

The then macro

The ?then macro can be used to chain (>>) monadic expressions of the same type. The second argument to ?then is automatically wrapped in a thunk that will only be executed if the first argument indicates success. For example (with list monad):

-include_lib("do/include/do.hrl").
maybe_add1(1) -> [2];
maybe_add1(_) -> [].
then_example() ->
[] = ?then([], maybe_add1(1)),
[] = ?then([5], maybe_add1(2)),
[2] = ?then([5], maybe_add1(1)).

The do macro

The ?do macro consecutively binds (>>= or >>) monads and functions. The macro takes a start value (a monad), and a list of functions. The functions must each take either 0 or 1 argument(s) and must return a monad. On execution, the start value is passed to the first function, and is then piped through consecutive functions using bind or then. For example (with maybe monad):

-include_lib("do/include/do.hrl").
maybe_add1(N) when N > 2 -> nothing;
maybe_add1(N) -> {just, N + 1}.
do_example() ->
nothing = ?do({just, 1}, [ fun maybe_add1/1,
fun maybe_add1/1,
fun maybe_add1/1,
fun maybe_add1/1]).