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 type ‘Int’ 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 of ‘Type1’, namely ‘(func a)’
In the expression: Type1 (func a)
x2.hs:4:39:
Couldn't match expected type ‘a’ with actual type ‘Int’
‘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 of ‘Type1’, 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