Search code examples
haskellfunctor

Functor declaration with multiple types?


I've got the following data type:

data Users id height weight = User id height weight

instance Functor Users where
fmap f (User id height weight) = User(f id height weight)

Yet this won't compile?

It works fine when I use a type with a single parameter, such as:

data Users id = User id
instance Functor Users where
fmap f (User id) = User (f id)

Why isn't my first example working?


Solution

  • Each type and type constructor has a kind. Something simple like Int has kind *. Your single-argument type constructor Users has kind * -> *; it takes one type and returns a new one. Your first example of Users has kind * -> * -> * -> *; it takes three types and returns a new one.

    Functor only works with type constructors of kind * -> *. This allows you to define a Functor instance for your second Users type constructor, but not your first one.

    Think about your first try: the data constructor Users takes three arguments, but your definition of fmap attempts to call it with just one, the return value of f. You could make Users a functor as long as you are willing to make all three fields the same type:

    data Users a = Users a a a
    
    instance Functor Users where
        fmap f (Users a b c) = Users (f a) (f b) (f c)
    

    In theory, you could define a class Trifunctor (there is a Bifunctor class available):

    class Trifunctor f where
        trimap :: (a1 -> b1) -> (a2 -> b2) -> (a3 -> b3) -> f a1 a2 a3 -> f b1 b2 b3
    
    data Users a b c = Users a b c
    
    instance Trifunctor Users where
        trimap f g h (Users a b c) = Users (f a) (g b) (h c)
    

    but it's debatable how useful this would be. Functor is useful for general purpose containers, because they have a wide range of uses. Users, on the other hand, seems pretty specific. You generally don't need the flexibility you are defining it with; data User = User Int Int Int seems like it would work fine, and there is no need to map a function over this: how often to you need the same function to modify height, weight, and age in the same way?