Difference between Monad and Applicative in Haskell

前端 未结 6 2044
有刺的猬
有刺的猬 2020-11-29 17:33

I just read the following from typeclassopedia about the difference between Monad and Applicative. I can understand that there is no join

6条回答
  •  [愿得一人]
    2020-11-29 17:55

    But the following description looks vague to me and I couldn't figure out what exactly is meant by "the result" of a monadic computation/action.

    Well, that vagueness is somewhat deliberate, because what "the result" is of a monadic computation is something that depends on each type. The best answer is a bit tautological: the "result" (or results, since there can be more than one) is whatever value(s) the instance's implementation of (>>=) :: Monad m => m a -> (a -> m b) -> m b invokes the function argument with.

    So, if I put a value into Maybe, which makes a monad, what is the result of this "computation"?

    The Maybe monad looks like this:

    instance Monad Maybe where
        return = Just
        Nothing >>= _ = Nothing
        Just a >>= k = k a
    

    The only thing in here that qualifies as a "result" is the a in the second equation for >>=, because it's the only thing that ever gets "fed" to the second argument of >>=.

    Other answers have gone into depth about the ifA vs. ifM difference, so I thought I'd highlight another significant difference: applicatives compose, monads don't. With Monads, if you want to make a Monad that combines the effects of two existing ones, you have to rewrite one of them as a monad transformer. In contrast, if you have two Applicatives you can easily make a more complex one out of them, as shown below. (Code is copypasted from transformers.)

    -- | The composition of two functors.
    newtype Compose f g a = Compose { getCompose :: f (g a) }
    
    -- | The composition of two functors is also a functor.
    instance (Functor f, Functor g) => Functor (Compose f g) where
        fmap f (Compose x) = Compose (fmap (fmap f) x)
    
    -- | The composition of two applicatives is also an applicative.
    instance (Applicative f, Applicative g) => Applicative (Compose f g) where
        pure x = Compose (pure (pure x))
        Compose f <*> Compose x = Compose ((<*>) <$> f <*> x)
    
    
    -- | The product of two functors.
    data Product f g a = Pair (f a) (g a)
    
    -- | The product of two functors is also a functor.
    instance (Functor f, Functor g) => Functor (Product f g) where
        fmap f (Pair x y) = Pair (fmap f x) (fmap f y)
    
    -- | The product of two applicatives is also an applicative.
    instance (Applicative f, Applicative g) => Applicative (Product f g) where
        pure x = Pair (pure x) (pure x)
        Pair f g <*> Pair x y = Pair (f <*> x) (g <*> y)
    
    
    -- | The sum of a functor @f@ with the 'Identity' functor
    data Lift f a = Pure a | Other (f a)
    
    -- | The sum of two functors is always a functor.
    instance (Functor f) => Functor (Lift f) where
        fmap f (Pure x) = Pure (f x)
        fmap f (Other y) = Other (fmap f y)
    
    -- | The sum of any applicative with 'Identity' is also an applicative 
    instance (Applicative f) => Applicative (Lift f) where
        pure = Pure
        Pure f <*> Pure x = Pure (f x)
        Pure f <*> Other y = Other (f <$> y)
        Other f <*> Pure x = Other (($ x) <$> f)
        Other f <*> Other y = Other (f <*> y)
    

    Now, if we add in the Constant functor/applicative:

    newtype Constant a b = Constant { getConstant :: a }
    
    instance Functor (Constant a) where
        fmap f (Constant x) = Constant x
    
    instance (Monoid a) => Applicative (Constant a) where
        pure _ = Constant mempty
        Constant x <*> Constant y = Constant (x `mappend` y)
    

    ...we can assemble the "applicative Either" from the other responses out of Lift and Constant:

    type Error e a = Lift (Constant e) a
    

提交回复
热议问题