I wrote an explanation of fmap vs bind but then I realized the problem here is that for some reason the function needs to be in the left hand side of the <|>
operator.
Functor.fmap
takes a value and then a function: MonadEx/functor.ex at master Β· rob-brown/MonadEx Β· GitHub
But <|>
reverses the order: MonadEx/operators.ex at master Β· rob-brown/MonadEx Β· GitHub
So I think your function should do (&plus_1/1) <|> (&plus_1/1) <|> success(x)
instead?
The explanation I started to write in case you want it:
The way to chain monads is by using bind
, not fmap
.
bind
's signature is bind :: m a -> (a -> m b) -> m b
where m
is the monad(the context). What this tells you is that you start with a monad m
with a type a
, you give it a function that works with an a
and returns a monad m
with a type mb
, and bind
will give you back that last m b
.
To illustrate it a bit better, if I have a value x
and I apply it a function a -> m b
, like something that returns a Maybe b
, and I want to apply that a -> m b
again, I would end up with Maybe (Maybe b)
. bind
lets you do composition while avoiding that nesting.
fmap
on the other hand is a way to lift a function to work in a context. Itβs signature is fmap :: (a -> b) -> (f a -> f b)
, which means that you start with a function from a
to b
and get back a lifted function that works in a context f
(the functor). This is essentially what you do with Enum.map
, you give it an list and it takes care of using the function in every element of the list and returns back a list. If you have a Maybe
, fmap
would lift the function to be applied to the element inside the Just
but skip the Nothing
.
Monads are indeed Functors in the sense that you can define fmap
in terms of bind
and return
(the functions in that monadex module):
fmap f m = m >>= (return . f)
The other altenative is defining bind
in terms of join
and fmap
.
The definition of fmap
doesnβt involve monads though, itβs just a way to lift a function to work in a particular context.
An fmap
definition for a result tuple would look like this:
def fmap({:ok, x}, f) do
{:ok, f.(x)}
end
def fmap({:error, x}, _) do
{:error, x}
end
While a definition of bind
would looke like this:
def bind({:ok, x}, f) do
f.(x)
end
def bind({:error, x}, _) do
{:error, x}
end
Notice that in fmap
we extract the x
from the tuple so it can work with f
(the a -> f a
lifting), and then it wraps the result of applying f
to x
in an ok tuple again (the b -> f b
lifting), essentially making the function f
work in the context of a result tuple. But in bind
the f
already returns a result tuple, so we donβt need to wrap it, otherwise it would result in nested result tuples.
More βcorrectβ definitions would be curried functions, though, so fmap
would be:
def fmap(f), do: fn
{:ok, x} -> {:ok, f.(x)}
{:error, x} -> {:error, x}
end
So fmap
here returns a version of f
that can work with result tuples. I think this is a better illustration of what lifting a function means.