使用数据和参数,而不是existentials来获取上下文
我想如果你想要
data Thing = Good [(Char,Int)] | Bad String | Indifferent Leg
但有时也
data Thing = Good [(Char,Float)] | Bad String | Indifferent Arm
你可以定义
data Thing num bodypart = Good [(Char,num)] | Bad String | Indifferent bodypart
或者如果你想确保num
总是数字,你可以做
data Num num => Thing num bodypart = Good [(Char,num)] | Bad String | Indifferent bodypart
最后,您可以bodypart
通过定义自己的类来限制允许的类型:
class Body a where
-- some useful function(s) here
instance Body Leg where
-- define useful function(s) on Legs
instance Body Arm
-- define useful function(s) on Arms
data (Num num,Body bodypart) => Thing num bodypart =
Good [(Char,num)] | Bad String | Indifferent bodypart
我想劝阻您不要通过 forall 构造函数或通过 GADT 使用存在类型,因为将num
参数添加到您的数据类型在实践中非常有用,即使它需要更多的输入。
类型同义词的限制?
请注意,当您使用类似的约束时
data (Num num) => Thing num = T [(Char,num)]
实际上只是将构造函数的类型更改T
为
T :: (Num num) => [(Char,num)] -> Thing num
而不是T :: [(Char,num)] -> Thing num
. 这意味着每次您使用T
时,都需要有一个 context (Num num)
,但这正是您想要的 - 阻止人们将数据放入您的非数字数据类型中。
这个事实的结果是你不能写
type Num num => [(Char,num)]
因为没有需要T
上下文的数据构造函数;(Num num)
如果我有 [('4',False)],它会自动匹配类型[(Char,num)]
,因为它是同义词。编译器在决定某事物是什么类型之前,不能在您的代码中运行以寻找实例。在这种data
情况下,它有一个T
告诉它类型的构造函数,它可以保证有一个Num num
实例,因为它检查了你对函数的使用T
。不T
,没有上下文。