문제

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?

도움이 되었습니까?

해결책

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
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 softwareengineering.stackexchange
scroll top