Search code examples
haskelltypesfunctor

Writing a generic functor instance across type constructors?


I'm learning basic type classes and have written my own implementation of functor for my type Test a (behaves just like Maybe):

data Test a = Test a | Emp

class FC c a where
  t :: (a -> b) -> c a -> c b

instance FC Test a where
  t f (Test a) = Test (f a) 
  t f (Emp) = Emp

instance FC Maybe a where
  t f (Just a) = Just (f a) 
  t f (Nothing) = Nothing

Is it possible to implement something like:

instance FC c where
  t f (c v) = c (f v)

Error:

Parse error in pattern: c

In other words, abstract away the type constructor, replace with c and v, therefore creating a general instance that can be applied to any value with a context?


Solution

  • As you've learned, c a is not a syntactically valid pattern. But reading your question instead as a feature proposal: How would that work? Not every Functor has a single-element constructor which can be mapped over according to your pattern. Some examples:

    data Pair a = Pair a a  -- more than one element
    instance Functor Pair where
        fmap f (Pair x y) = Pair (f x) (f y)
    
    data Proxy a = Proxy  -- no elements
    instance Functor Proxy where
        fmap f Proxy = Proxy
    
    newtype Cont r a = Cont { runCont :: (a -> r) -> r }  -- element appears in a double-negative position
    instance Functor (Cont r) where
        fmap f (Cont g) = Cont (g . (. f))
    

    In any case, I don't think the idea of a "generic instance" really makes sense. The instance is where you put your type-specific code. (It has to go somewhere!)

    If you want to exert less effort in writing Functor instances you can use GHC's DeriveFunctor extension.

    {-# LANGUAGE DeriveFunctor #-}
    
    data Pair a = Pair a a deriving Functor
    
    data Proxy a = Proxy deriving Functor
    
    newtype Cont r a = Cont { runCont :: (a -> r) -> r } deriving Functor