Why does the 2-tuple Functor instance only apply the function to the second element?

前端 未结 3 1073
闹比i
闹比i 2020-12-05 17:12
import Control.Applicative

main = print $ fmap (*2) (1,2)

produces (1,4). I would expect it it to produce (2,4) but inst

3条回答
  •  爱一瞬间的悲伤
    2020-12-05 17:53

    Pairs are, essentially, defined like this:

    data (,) a b = (,) a b
    

    The Functor class looks like this:

    class Functor f where
      fmap :: (a -> b) -> f a -> f b
    

    Since the types of function arguments and results must have kind * (i.e. they represent values rather than type functions that can be applied further or more exotic things), we must have a :: *, b :: *, and, most importantly for our purposes, f :: * -> *. Since (,) has kind * -> * -> *, it must be applied to a type of kind * to obtain a type suitable to be a Functor. Thus

    instance Functor ((,) x) where
      -- fmap :: (a -> b) -> (x,a) -> (x,b)
    

    So there's actually no way to write a Functor instance doing anything else.


    One useful class that offers more ways to work with pairs is Bifunctor, from Data.Bifunctor.

    class Bifunctor f where
      bimap :: (a -> b) -> (c -> d) -> f a c -> f b d
      bimap f g = first f . second g
    
      first :: (a -> b) -> f a y -> f b y
      first f = bimap f id
    
      second :: (c -> d) -> f x c -> f x d
      second g = bimap id g
    

    This lets you write things like the following (from Data.Bifunctor.Join):

      newtype Join p a =
        Join { runJoin :: p a a }
    
      instance Bifunctor p => Functor (Join p) where
        fmap f = Join . bimap f f . runJoin
    

    Join (,) is then essentially the same as Pair, where

    data Pair a = Pair a a
    

    Of course, you can also just use the Bifunctor instance to work with pairs directly.

提交回复
热议问题