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 | C
Yes there is a tension between types and data... which by the way shows how thin is the line.
The pratical answer is to use a default instance as indicated in the Haskell Wiki. It does answer your exact question since you must give up direct constructor use.
Thus for your example,
data Age = Baby | Child | PreTeen | Adult | NoAge
data Clothing = Pants {gender :: Gender, age :: Age}
| Shirt {gender :: Gender, age :: Age}
| Skirt {gender :: Gender, age :: Age}
deriving (Show, Eq)
skirt = Skirt { gender=Female, age=NoAge }
then developpers can create new instances with default values, using the copy-and-update facility of the record syntax
newSkirt = skirt { age=Adult }
and gender newSkirt
evaluates to Female
I want to stress that this approach leads you to define default values at the type level, which I think is a Good Thing (of course the NoAge
constructor is the Nothing
of a Maybe Age
type).