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.())

#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.


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\$ Jul 22 '15 at 11:45
  • \$\begingroup\$ Again - bind should be implemented differently, depending on the type of monad you are using. The function f passed to bind on the other hand is part of the user's logic. For example find_user |> bind(&authenticate_user/1) |> bind(&render_response/1) - assuming this is the maybe monad you'd expect find_user, authenticate_user, render_response to be functions provided by the user of your monad API and returning {:just, _} or :nothing. \$\endgroup\$ Jul 22 '15 at 11:54
  • 2
    \$\begingroup\$ Also check out MonadEx. \$\endgroup\$
    – rbrown
    Jul 22 '15 at 14:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.