This is my first crack at a identity monad and a maybe monad in Elixir:
defmodule Monad do
@doc "v is value to be wrapped as monadic value"
def return(v), do: fn -> v end
@doc "m is monad function, mv is monadic value"
def bind(m, mv), do: m.(mv.())
end
#Simple identity monad
id = fn s -> s end
h = Monad.return(:ok)
r = Monad.bind(id, h)
#Maybe monad
maybe = fn s -> if is_nil(s), do: :error, else: s end
r = Monad.bind(maybe, h)
I'd like to see if there is a way to make the monad into a protocol since the return and bind seem to be common to all monads.
1 Answer 1
I think you're sort of missing the point with this code - the difference between different kinds of monads (Maybe vs. List vs. Identity, etc.) is in the different implementations of return
and bind
. I think a good starting place might be reading through a library that implements monads in elixir, like monad. Looking into that you can see that for example Maybe
is implemented like this (simplifying):
def return(x), do: {:just, x}
def bind({:just, x}, f), do: f.(x)
def bind(:nothing, _), do: :nothing
def fail(_), do: :nothing
While for example Error
(the second-simplest I think) is implemented like this:
def return(x), do: {:ok, x}
def bind(e = {:error, _}, _), do: e
def bind({:ok, x}, f), do: f.(x)
def fail(msg), do: {:error, msg}
The difference between the two being that in one case you only get information that there is no result (:nothing
) while in the other you either get a result or some information on the error (represented by {:error, _}
). Also note that both have additional helper functions to for example extract the result (presumably at the end of the computation).
-
\$\begingroup\$ I'm trying to follow your comments. You're saying I should make the code less generic? I understand that I might change the return function to get different behavior there but the bind can be easily parameterized by the function which is passed in so I'm not sure I understand how making it less generic is an improvement. \$\endgroup\$Onorio Catenacci– Onorio Catenacci2015年07月22日 11:45:23 +00:00Commented Jul 22, 2015 at 11:45
-
\$\begingroup\$ Again -
bind
should be implemented differently, depending on the type of monad you are using. The functionf
passed tobind
on the other hand is part of the user's logic. For examplefind_user |> bind(&authenticate_user/1) |> bind(&render_response/1)
- assuming this is the maybe monad you'd expectfind_user
,authenticate_user
,render_response
to be functions provided by the user of your monad API and returning{:just, _}
or:nothing
. \$\endgroup\$Paweł Obrok– Paweł Obrok2015年07月22日 11:54:13 +00:00Commented Jul 22, 2015 at 11:54 -
2