Home > Enterprise >  How do you write a Functor on a data type that uses concrete types?
How do you write a Functor on a data type that uses concrete types?

Time:10-18

I'm trying to get into Haskell from the world of GO, so am almost certainly missing something trivial but this has me stumped!

The following compiles but does nothing useful.

data Thing a = Type1 Int | Type2 Int Int deriving Show

instance Functor Thing where
    fmap func (Type1 a) = Type1 a
    fmap func (Type2 a b) = Type2 a b

As soon as I try to actually use the "func", it wont compile. For example, just using "func" in the first of the two pattern rules like this..

data Thing a = Type1 Int | Type2 Int Int deriving Show

instance Functor Thing where
    fmap func (Type1 a) = Type1 (func a)
    fmap func (Type2 a b) = Type2 a b

Gives the following error but if I declare "Thing" using "Type1 a | Type2 a a" then it compiles ok.

x2.hs:4:34:
    Couldn't match expected typeInt’ with actual type ‘b’
      ‘b’ is a rigid type variable bound by
          the type signature for fmap :: (a -> b) -> Thing a -> Thing b
          at x2.hs:4:5
    Relevant bindings include
      func :: a -> b (bound at x2.hs:4:10)
      fmap :: (a -> b) -> Thing a -> Thing b (bound at x2.hs:4:5)
    In the first argument ofType1’, namely ‘(func a)’
    In the expression: Type1 (func a)

x2.hs:4:39:
    Couldn't match expected type ‘a’ with actual typeInt
      ‘a’ is a rigid type variable bound by
          the type signature for fmap :: (a -> b) -> Thing a -> Thing b
          at x2.hs:4:5
    Relevant bindings include
      func :: a -> b (bound at x2.hs:4:10)
      fmap :: (a -> b) -> Thing a -> Thing b (bound at x2.hs:4:5)
    In the first argument of ‘func’, namely ‘a’
    In the first argument ofType1’, namely ‘(func a)’

Can anyone explain why and what I can do to fix it? Is it possible to use concrete types in a data definition and then use that in a Functor or are we limited to using generic type variables (excuse me if I'm using the wrong words here)?

CodePudding user response:

Is it possible to use concrete types in a data definition and then use that in a Functor or are we limited to using generic type variables (excuse me if I'm using the wrong words here)?

You are not limited. However, only fields whose type is (or mentions) the last generic variable in the data declaration will be able to be modified in your Functor instance. (Some modifications to fields with other types are allowed by the type-checker; but all will cause the functor laws to be violated.) So, for example, this has a mix of generic and concrete fields:

data Thing a = Type1 Int a | Type2 Int Int

instance Functor Thing where
    fmap f (Type1 n a) = Type1 n (f a)
    fmap f (Type2 m n) = Type2 m n

Of course, there is nothing stopping you from writing another function, not named fmap, that does whatever you like. So, this is also fine:

data Thing = Type1 Int | Type2 Int Int

notFmap :: (Int -> Int) -> Thing -> Thing
notFmap f (Type1 n) = Type1 (f n)
notFmap f (Type2 m n) = Type2 m n
  • Related