Say I have the following:
data Type
= StringType
| IntType
| FloatType
data Op
= Add Type
| Subtract Type
I'd like to constrain the possible types under Subtract
, such that it only allows for int or float. In other words,
patternMatch :: Op -> ()
patternMatch (Add StringType) = ()
patternMatch (Add IntType) = ()
patternMatch (Add FloatType) = ()
patternMatch (Subtract IntType) = ()
patternMatch (Subtract FloatType) = ()
Should be an exhaustive pattern match.
One way of doing this is to introduce separate datatypes for each operation, where it only has the allowed subtypes:
newtype StringType = StringType
newtype IntType = IntType
newtype FloatType = FloatType
data Addable = AddableString StringType | AddableInt IntType | AddableFloat FloatType
data Subtractable = SubtractableInt IntType | SubtractableFloat FloatType
data Op = Add Addable | Subtract Subtractable
However, this makes things a lot more verbose, as we have to create a new constructor name for each category. Is there a way to 'restrict' the possible constructors within a type without making an explicit subset?
Would this shorter with the use of DataKinds
? I'm a bit unsure as to how to make it more concise than just specifying new data for each constraint.
This question is an extension of my original question, where I asked about datakind unions. There were lots of good suggestions there, but unfortunately the unions don't work when pattern matching; the compiler will still complain that the patterns are not exhaustive.