我正在努力了解类型系列而没有太大的成功。这是一个最小的例子:
{-# LANGUAGE TypeFamilies #-}
class Object obj where
type Unit obj :: *
unit :: Unit obj
instance (Object obj, Object obj') => Object (obj, obj') where
type Unit (obj, obj') = (Unit obj, Unit obj')
unit = (unit, unit)
我认为目的是相当透明的(尝试定义产品类别)。
这给了我:
objs.hs:10:10:
Could not deduce (Unit obj' ~ Unit obj1)
from the context (Object obj, Object obj')
bound by the instance declaration at objs.hs:8:10-56
NB: `Unit' is a type function, and may not be injective
The type variable `obj1' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Expected type: Unit (obj, obj')
Actual type: (Unit obj0, Unit obj1)
In the expression: (unit, unit)
In an equation for `unit': unit = (unit, unit)
In the instance declaration for `Object (obj, obj')'
我尝试添加类型签名:
unit = (unit :: Unit obj, unit :: Unit obj')
但这只会让事情变得更糟。
以下修改编译:
{-# LANGUAGE TypeFamilies #-}
class Object obj where
type Unit obj :: *
unit :: obj -> Unit obj
instance (Object obj, Object obj') => Object (obj, obj') where
type Unit (obj, obj') = (Unit obj, Unit obj')
unit (o, o') = (unit o, unit o')
但我不喜欢unit
的多余论点。
是否可以定义无参数unit
?
答案 0 :(得分:21)
你正在尝试做的事情对GHC来说很棘手,因为GHC在错误信息中说,类型系列确实不需要是单射的。
如果F
暗示F x ~ F y
,则类型函数x ~ y
称为单射函数。如果F
是通过data
定义的普通类型构造函数,那么这总是正确的。但是,对于类型系列,它不成立。
例如,根据您对Object
的定义,定义以下实例没有问题:
instance Object Int where
type Unit Int = Int
unit = 0
instance Object Char where
type Unit Char = Int
unit = 1
现在,如果你写unit :: Int
,那么GHC怎么可能确定它应该评估为0
还是1
?甚至不写unit :: Unit Int
使得它更加清晰,因为
Unit Int ~ Int ~ Unit Char
所以这三种类型都应该是可以互换的。由于Unit
不能保证是唯一的,因此根本无法从Unit x
x
的知识中得出唯一的结论......
结果是unit
可以定义,但不能使用。
您已经列出了解决此问题的最常用方法。通过将类型签名更改为
,添加一个帮助GHC实际确定相关类型参数的参数unit :: obj -> Unit obj
或
unit :: Proxy obj -> Unit obj
适用于Proxy
的定义,例如
data Proxy a
一个可能鲜为人知的选择是你可以向GHC证明你的类型函数是可逆的。
这样做的方法是定义一个反型家庭
type family UnUnit obj :: *
并使可逆性成为类型类的超类约束:
class (UnUnit (Unit obj) ~ obj) => Object obj where
type Unit obj :: *
unit :: Unit obj
现在你必须做额外的工作。对于类的每个实例,您必须定义
Unit
的实际倒数正确。例如,
instance (Object obj, Object obj') => Object (obj, obj') where
type Unit (obj, obj') = (Unit obj, Unit obj')
unit = (unit, unit)
type instance UnUnit (obj, obj') = (UnUnit obj, UnUnit obj')
但鉴于此修改,定义类型检查。现在,如果GHC在某个特定类型unit
遇到T
并且想要确定S
类型Unit S ~ T
,那么它可以应用超类约束来推断< / p>
S ~ UnUnit (Unit S) ~ UnUnit T
如果我们现在尝试为Object Int
和Object Char
定义错误的实例,这两个实例都将Unit Int
和Unit Char
都映射为Int
,那是行不通的,因为我们必须决定UnObject Int
应该是Int
还是Char
,但是不能同时...... {/ p>
答案 1 :(得分:3)
由于关联类型不是单射的(在下面定义),因此需要一个参数才能键入check。例如,请考虑以下(不正确的)代码。
class Valuable o where
type Value o :: *
value :: Value o
data Pearl
data Diamond
instance Valuable Pearl where
type Value Pearl = Int
value = 1000
instance Valuable Diamond where
type Value Diamond = Int
value = 10000
请注意,Value a ~ Value b
并不意味着a ~ b
因为注入会产生它。因此,现在value
的价值是多么模糊。如果我们从value :: Int
开始限制Value Pearl ~ Value Diamond ~ Int
的类型,它甚至无济于事。
也就是说,代码中存在一些非常好的并行性。
import Control.Arrow
class Object obj where
type Unit obj :: *
unit :: obj -> Unit obj
instance (Object obj, Object obj') => Object (obj, obj') where
type Unit (obj, obj') = (Unit obj, Unit obj')
unit = unit *** unit