Suppose that two new types are defined like this
type MyProductType a = (FType1 a, FType2 a)
type MyCoproductType a = Either (FType1 a) (FType2 a)
...and that FType1
and Ftype2
are both instances of Functor
.
If one now were to declare MyProductType
and MyCoproductType
as instances of Functor
, would the compiler require explicit definitions for their respective fmap
's, or can it infer these definitions from the previous ones?
Also, is the answer to this question implementation-dependent, or does it follow from the Haskell spec?
By way of background, this question was motivated by trying to make sense of a remark in something I'm reading. The author first defines
type Writer a = (a, String)
...and later writes (my emphasis)
...the
Writer
type constructor is functorial ina
. We don't even have to implementfmap
for it, because it's just a simple product type.
The emphasized text is the remark I'm trying to make sense of. I thought it meant that Haskell could infer fmap
's for any ADT based on functorial types, and, in particular, it could infer the fmap
for a "simple product type" like Writer
, but now I think this interpretation is not right (at least if I'm reading Ørjan Johansen's answer correctly).
As for what the author meant by that sentence, now I really have no clue. Maybe all he meant is that it's not worth the trouble to re-define Writer
in such a way that its functoriality can be made explicit, since it's such a "simple ... type". (Grasping at straws here.)
First, you cannot generally define new instances for type
synonyms, especially not partially applied ones as you would need in your case. I think you meant to define a newtype
or data
instead:
newtype MyProductType a = MP (FType1 a, FType2 a)
newtype MyCoproductType a = MC (Either (FType1 a) (FType2 a))
Standard Haskell says nothing about deriving Functor
automatically at all, that is only possible with GHC's DeriveFunctor
extension. (Or sometimes GeneralizedNewtypeDeriving
, but that doesn't apply in your examples because you're not using a
just as the last argument inside the constructor.)
So let's try that:
{-# LANGUAGE DeriveFunctor #-}
data FType1 a = FType1 a deriving Functor
data FType2 a = FType2 a deriving Functor
newtype MyProductType a = MP (FType1 a, FType2 a) deriving Functor
newtype MyCoproductType a = MC (Either (FType1 a) (FType2 a)) deriving Functor
We get the error message:
Test.hs:6:76:
Can't make a derived instance of ‘Functor MyCoproductType’:
Constructor ‘MC’ must use the type variable only as the last argument of a data type
In the newtype declaration for ‘MyCoproductType’
It turns out that GHC can derive the first three, but not the last one. I believe the third one only works because tuples are special cased. Either
doesn't work though, because GHC doesn't keep any special knowledge about how Either
treats its first argument. It's nominally a mathematical functor in that argument, but not a Haskell Functor
.
Note that GHC is smarter about using variables only as last argument of types known to be Functor
s. The following works fine:
newtype MyWrappedType a = MW (Either (FType1 Int) (FType2 (Maybe a))) deriving Functor
So to sum up: It depends, GHC has an extension for this but it's not always smart enough to do what you want.