As I was writing up an answer just now, I ran across an interesting problem:
data Gender = Male | Female
deriving (Eq, Show)
data Age = Baby | Child | PreTeen | Adult
deriving (Eq, Show, Ord)
data Clothing = Pants Gender Age
| Shirt Gender Age
| Skirt Age -- assumed to be Female
deriving (Show, Eq)
Suppose I wish to write the final data type with record syntax:
data Clothing = Pants {gender :: Gender, age :: Age}
| Shirt {gender :: Gender, age :: Age}
| Skirt {age :: Age}
deriving (Show, Eq)
The problem is, I want gender $ Skirt foo to always evaluate to Female (regardless of foo, which is an Age). I can think of a few ways to accomplish this, but they require that I either
- use smart constructors, theoretically allowing
Skirt Male foobut not exposing Constructors - define my own
genderfunction
With #1, by not exposing the constructor in the module, I effectively prevent users of the module from taking advantage of record syntax. With #2, I have to forego record syntax entirely, or define an additional function gender', which again defeats record syntax.
Is there a way to both take advantage of record syntax, and also provide a “default”, unchangeable value for one of my constructors? I am open to non-record-syntax solutions as well (lenses, perhaps?) as long as they are just as elegant (or moreso).
In the absence of a convincing counterexample, the answer seems to be “no”.