HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Simple/Naive Implementation of Identity and Maybe Monad in Elixir

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
identitysimplenaiveelixirmaybeandimplementationmonad

Problem

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.

Solution

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

Code Snippets

def return(x), do: {:just, x}

def bind({:just, x}, f), do: f.(x)
def bind(:nothing, _), do: :nothing

def fail(_), do: :nothing
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}

Context

StackExchange Code Review Q#97631, answer score: 3

Revisions (0)

No revisions yet.