Monad is an enhanced version of the Applicative functor.

I’m kind of forgetting what the Applicative functor is, so LET me review it.

review

Functor type class

The types that support fmap are functors, like Maybe fmap is defined as (a -> b) -> F A -> F b and it means to take the values in a container, process them with a function, and put them back into the same container.

Applicative type class

Types that support pure and <*> are instances of applicative, such as Maybe pure is defined as a -> f a, What it does is it puts a value in a container and the definition of <*> is f (a -> b) -> f a -> f b which is very similar to fmap except that that function is also in a container.

But sometimes the container metaphor isn’t so useful, like the “(->) R is also an instance of Application” part.

Say it again from a different Angle

Hypothesis one, we have

  • A plain value of type A
  • The ordinary function A -> B
  • You want a return value of type B

Call a normal function directly.

Hypothesis two, we have

  • A unique value of type Maybe A
  • The ordinary function A -> B
  • You want a return value of type Maybe B

So Maybe satisfies the fmap criteria and then calls fmap and normal functions

ghci> fmap (++ "!") (Just "wisdom")
Just "wisdom!"
ghci> fmap (++ "!") Nothing
Nothing
Copy the code

Hypothesis three, we have

  • A unique value of type Maybe A
  • Maybe (A -> B)
  • You want a return value of type Maybe B

So we ask Maybe to satisfy pure and <*> and then call <*> and fancy functions

ghci> Just (+3) < * >Just 3 
Just 6
ghci> Nothing< * >Just "greed"
Nothing
ghci> Just ord <*> Nothing
Nothing
Pure means to make normal functions work
-- Max <$> is equivalent to pure Max <*>
ghci> max <$> Just 3< * >Just6
Just 6
ghci> max <$> Just 3< * >Nothing
Nothing
Copy the code

Hypothesis four, we have

  • A unique value of type Maybe A
  • Function A -> Maybe B (if the function is A -> B, then it is easy to change to A -> Maybe B because B -> Maybe B is easy to implement)
  • You want a return value of type Maybe B

That’s the problem Monad is trying to solve.

How do you do that? Let’s use Maybe as an example (because Maybe is actually monad).

To simplify things a bit:

  • A plain value of type A
  • Function A -> Maybe B
  • You want a return value of type Maybe B

This is as simple as calling the function directly.

Next, consider what additional things we would need to do if we changed the argument from “normal value of type A” to “odd value of type Maybe A.”

Obviously, if the argument is Just A, the function is called with the value of A; If the argument is Nothing, Nothing is returned.

So the implementation is as follows:

applyMaybe: :Maybe a -> (a -> Maybe b) -> Maybe b 
applyMaybe Nothing f = Nothing
applyMaybe (Just x) f = f x
Copy the code

Haskell calls applyMaybe >>=.

Monad type class definition

class Monad m where
    return :: a -> m a  -- Just like pure
    
    (>>=) :: m a -> (a -> m b) -> m b
    
    -- The book says forget the >> and fail
    (>>)  :: m a -> m b -> m b
    x >> y =   x >>= \_ -> y 
    
    fail :: String -> m a
    fail msg = error msg
    
Copy the code

Theoretically, before a type becomes an instance of Monad, it should become an instance of Applicative, just as class (Functor f) => Applicative f where. But here the class Monad does not appear (Applicative M) why?

The book says it’s because “when Haskell was designed, the Applicative functor was not considered to be so useful.” Okay, I believe you.

Maybe is an instance of Monad

instance Monad Maybe where
    return x = Just x
    Nothing >>= f = Nothing
    Just x >>= f = f x
    fail _ = Nothing
Copy the code

Use the

ghci> return "WHAT": :Maybe String
Just "WHAT"
ghci> Just 9 >>= \x -> return (x*10) -- Note that return does not exit
Just 90
ghci> Nothing >>= \x -> return (x*10)
Nothing
Copy the code

What’s the point of Monad?

The above said

Hypothesis four, we have

  • A unique value of type Maybe A
  • Function A -> Maybe B
  • You want a return value of type Maybe B

That’s the problem Monad is trying to solve.

So why are we doing this?

The book gives you an example, and I’ll sketch it here.

Maybe A has two possibilities: Just A, Nothing.

We regard Just A as successful A and Nothing as failure.

Then A -> Maybe B is A function that can handle successful A and return either successful B or failure.

If there is another function B -> Maybe C, you can continue with successful B.

It’s like Promise!

  • Parameters forPromise<User>
  • Function forUser -> Promise<Role>Most of the time our function is User -> Role, but we want to change Role toPromise<Role>Promise. Resolve
  • And then we can connect the top two things and getPromsie<Role>!

But notice THAT I didn’t say that Promise was Monad, and I don’t know if that’s true yet.

The corresponding Haskell code looks something like this

return A> > =AToMaybeB> > =BToMaybeC> > =CToMaybeD 
Copy the code

Corresponding JS code

PromiseUser.then(UserToRole).then(RoleToElse)
Copy the code

Do grammar

I have in ordinary functions

let x=3; y="!" in show x ++ y
Copy the code

If you put both x and y in Maybe and use >>=, it goes like this

Just 3 >>= (\x -> 
    Just "!" >>= (\y -> 
        Just (show x ++ y )))
Copy the code

So to get rid of this messy notation, we can use do

foo: :Maybe String
foo = do
    x <- Just 3
    y <- Just "!"
    Just (show x++y )
Copy the code

So do is just a syntax for stringing monad values together. (Yes IO is monad)

Do requires a monad value to the right of each line <-. Each line in the example above is a Maybe value.

Pattern matching with Fail

justH: :Maybe Char
justH = do
    (x:xs) <- Just "hello"
    return x
Copy the code

The final value of justH is Just ‘h’. But what happens if the pattern match fails?

wopwop: :Maybe Char
wopwop = do
    (x:xs) <- Just ""
    return x
Copy the code

“” is an empty list, there’s no way to get x, then mONAL’s fail is called, and the default implementation of fail is

fail: : (Monad m) => String -> m a
fail msg = error msg
-- But Maybe's Fail works like this
fail _ = Nothing
Copy the code

So if the match fails, you get Nothing to keep your program from crashing, which is pretty neat.

A list is Monad

instance Monad [] where
    return x         = [x]
    xs >>= f        = concat (map f xs)
    fail _            = []
Copy the code

Example:

ghci> [3.4.5] >>= \x -> [x,x]
[3.- 3.4.4 -.5.- 5]
ghci> [] >>= \x -> ["bad"."mad"."rad"]
[]
ghci> [1.2.3] >>= \x -> []
[]
ghci> [1.2] >>= \n -> ['a','b'] >>= \ch -> return(n,ch)
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]
Let me rewrite it with do
listOfTuples: : [(Int.Char)]
listOfTuples = do
    n<-[1.2]
    ch<-['a','b']
    return (n,ch)
Copy the code

The laws of monad

Haskell cannot check whether a type satisfies Monad’s law, so the developer needs to make sure.

  1. Left identity element law —return x >>= fThe values of must andf xThe same
  2. Right identity element law —m >>= returnmMust have the same value
  3. Associative law —(m >>= f) >>= gAnd m>>= (\x -> f x >>= g)The same