haskellmonads

Haskell: why does 'id' make this function no longer monadic?


I am trying to understand why adding id in the last line of the sequence below removes the monadic aspect:

Prelude> :t id
id :: a -> a
Prelude> :t Control.Monad.liftM2
Control.Monad.liftM2
  :: Monad m => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
Prelude> :t  (==)
(==) :: Eq a => a -> a -> Bool
Prelude> :t Control.Monad.liftM2 (==)
Control.Monad.liftM2 (==)
  :: (Monad m, Eq a) => m a -> m a -> m Bool
Prelude> :t Control.Monad.liftM2 (==) id
Control.Monad.liftM2 (==) id :: Eq a => (a -> a) -> a -> Bool
Prelude>

How does adding id :: a -> a change the signature in the way it does in the last line ?


Solution

  • You’re fixing the type to a particular Monad instance, namely the “function reader” monad (instance Monad ((->) a)).

    id :: a -> a and you are attempting to use it as an argument to a parameter of type m a, so:

    m a  ~  a -> a
    m a  ~  (->) a a
    m a  ~  ((->) a) a
    m    ~  (->) a
    a    ~  a
    

    The remainder of the signature is:

    m a -> m Bool
    

    And since m ~ (->) a, the resulting type is:

    (->) a a -> (->) a Bool
    (a -> a) -> (a -> Bool)
    (a -> a) -> a -> Bool
    

    (Plus the Eq a constraint from the use of ==.)

    This is useful in pointfree code, particularly using the Applicative instance, since you can implicitly “spread” the argument of a function to subcomputations:

    nextThree = (,,) <$> (+ 1) <*> (+ 2) <*> (+ 3)
    -- or
    nextThree = liftA3 (,,) (+ 1) (+ 2) (+ 3)
    
    nextThree 5 == (6, 7, 8)
    
    uncurry' f = f <$> fst <*> snd
    -- or
    uncurry' f = liftA2 f fst snd
    
    uncurry' (+) (1, 2) == 3