6

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?

  • This is one of Haskell's weak spots. If you're feeling confident you could unsafeCoerce the difference away, but I wouldn't advise it. – Benjamin Hodgson Jun 12 '16 at 12:53
7

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
4

You can use the DeriveFunctor extension and just write

data Check a = Valid | Invalid | Unsure a deriving Functor

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.