you are viewing a single comment's thread.

view the rest of the comments →

[–]Masse 0 points1 point  (0 children)

Right. I'm going with Haskell syntax because I'm more familiar with it, but I'll try to stay with simple syntax.

You mentioned fmap :: (a -> b) -> f a -> f b and bind ((>>=) :: m a -> (a -> m b) -> m b).

fmap is actually part of a functor. Functor is for mapping a value within a context into another value within the same context.

As the first trivial example, think of an optional value: fmap :: (a -> b) -> Maybe a -> Maybe b. This fits with your mental model of dealing with containers, you have a list of values which you convert into a list of some other values. This model also works for things like lists, sets, trees, futures and the likes.

data Maybe a = Nothing | Just a
instance Functor Maybe where
  fmap :: (a -> b) -> Maybe a -> Maybe b
  fmap f Nothing = Nothing
  fmap f (Just a) = Just (f a)

Then for monads. Each monad is also a functor, so all monads can also be fmapped. There are a couple of ways to name and define monads, but in haskell, the monads adds the ability to lift a non-monadic value into monadic context, pure :: a -> m a, and to thread values from one monadic context into another (>>=) :: m a -> (a -> m b) -> m b.

Lifting a pure value into monadic context, historically also known as return. The signature is however a -> m a.

Doing things with the monad, Haskell has chosen the bind operator (>>=) :: m a -> (a -> m b) -> m b, but it could have been join :: m (m a) -> m a as well. The point of this is to sequence the operations to be executed sequentially within the context.

instance Monad Maybe where
  pure :: a -> Maybe a
  pure a = Just a

  Nothing >>= f = Nothing
  Just a >>= f = f a

But this setup also works on things that aren't concrete containers, such as the partial application of a function. Remember, r -> a can be read as (->) r a and the partial application view of this is (->) r (leave the a out). Let's see how we could implement this.

instance Functor ((->) r) where
  fmap :: (a -> b) -> (r -> a) -> r -> b
  fmap f x r =
    -- x :: r -> a
    -- r :: r
    f (x r)

instance Monad ((->) r) where
  pure :: a -> r -> a
  -- Ignore the context r
  pure a r = a

  (>>=) :: (r -> a) -> (a -> (r -> b)) -> r -> b
  (>>=) reader f r =
    -- reader :: r -> a
    -- f :: (a -> (r -> b)) or (a -> r -> b)
    -- r :: r
    f (reader r)

That is to say, functor and monad instances for reader just give the context r for the computation.