Suppose I have a parametrized data type with more than one nullary (constant) data constructor, such as:
data Check a = Valid | Invalid | Unsure a
Sometimes I want to manipulate the non-constant constructors with a function that keeps the nullary constants fixed, and I hope to do it like
instance Functor Check where
fmap f (Unsure a) = Unsure (f a)
fmap f c = c
This fails to typecheck because in the declaration fmap f c = c
, if f :: a -> b
then c
needs to be of both types Check a
and Check b
. Of course I could just write it out fully into
fmap f (Unsure a) = Unsure (f a)
fmap f Valid = Valid
fmap f Invalid = Invalid
which works but gets very unwieldy when there are many nullary constructors. Or, in some cases I could probably derive instances of Show
and Read
and use read (show c)
to coerce c
to the necessary type. But is there a more elegant way to approach this?
1
One thing you could do is change the definition of your datatype to factour out the Valid
/ Invalid
part like so:
data Status = StatusValid | StatusInvalid
data Check a = Sure Status | Unsure a
You can then implement Functor
without having to inspect the Status
:
instance Functor Check where
fmap f (Unsure a) = Unsure (f a)
fmap _ (Sure s) = Sure s
If you are willing to use the PatternSynonyms
extension (just stick {-# LANGUAGE PatternSynonyms #-}
at the top of your file), you can even recover Valid
/ Invalid
:
pattern Valid = Sure StatusValid
pattern Invalid = Sure StatusInvalid
You can use the DeriveFunctor extension and just write
data Check a = Valid | Invalid | Unsure a deriving Functor