我在Haskell的几个不同的地方遇到了一个名为TypeCast
的类型类。
这是相当神秘的,我似乎无法完全解析它。
class TypeCast a b | a -> b, b -> a where typeCast :: a -> b
class TypeCast' t a b | t a -> b, t b -> a where typeCast' :: t -> a -> b
class TypeCast'' t a b | t a -> b, t b -> a where typeCast'' :: t -> a -> b
instance TypeCast' () a b => TypeCast a b where typeCast x = typeCast' () x
instance TypeCast'' t a b => TypeCast' t a b where typeCast' = typeCast''
instance TypeCast'' () a a where typeCast'' _ x = x
http://okmij.org/ftp/Haskell/typecast.html对此代码提供了有用但却敷衍的评论。有关详细信息,该页面指向http://homepages.cwi.nl/~ralf/HList/paper.pdf,这是一个断开的链接。
我看到TypeCast
是一个允许您从一种类型转换为另一种类型的类,但我不明白为什么我们需要TypeCast'
和TypeCast''
。
看起来所有这些代码都允许您将类型转换为自身。在我看过的一些示例代码中,我尝试将其替换为:
class TypeCast a b | a -> b, b -> a where typeCast :: a -> b
instance TypeCast a a where typeCast a = a
并且样品仍然有效。我一直在看的样本主要来自第一个链接。
我想知道是否有人可以解释六条线的用途。
答案 0 :(得分:5)
TypeCast实际上是什么用?
它不用于检索有关存在类型的类型信息(这会破坏类型系统,因此不可能)。要理解TypeCast
,我们首先要了解有关haskell类型系统的一些特定细节。考虑以下激励示例:
data TTrue
data TFalse
class TypeEq a b c | a b -> c
instance TypeEq x x TTrue
instance TypeEq x y TFalse
这里的目标是在类型级别上有一个布尔标志 - 它告诉你两个类型是否相等。您可以使用~
来表示类型等价 - 但这只会使您在类型上失败(即Int ~ Bool
不编译而不是TypeEq Int Bool r
将r ~ TFalse
作为推断类型)。但是,这不会编译 - 功能依赖性冲突。原因很简单 - x x
只是x y
的实例化(即x ~ y
=> x y == x x
),因此根据fundeps的规则(请参阅完整的文档)规则的细节),这两个实例必须具有c
的相同值(或者这两个值必须是彼此的实例 - 它们不是)。
TypeEq
库中存在HList
类 - 让我们来看看它是如何实现的:
class HBool b => TypeEq x y b | x y -> b
instance TypeEq x x HTrue
instance (HBool b, TypeCast HFalse b) => TypeEq x y b
-- instance TypeEq x y HFalse -- would violate functional dependency
当然这些实例不会发生冲突 - HTrue
是b
的实例化。可是等等! TypeCast HFalse b
是否b
必须 HFalse
?是的,确实如此,但编译器在尝试解决fundep冲突时没有检查类实例约束。这是允许此类存在的关键“功能”。
简要说明 - 两个实例仍然重叠。但是对于-XUndecidableInstances -XOverlappingInstances
,编译器将首先选择第一个实例 ,因为第一个实例更具“特异性”(在这种情况下,这意味着它最多只有2个唯一)类型 - x
和HTrue
,而另一个实例最多有3个)。您可以找到UndecidableInstances
在文档中使用的完整规则集。
为什么TypeCast按原样编写?
如果你查看HList的源代码,有TypeCast
的多个实现。一个实现是:
instance TypeCast x x
一个人认为可行的直截了当的实例。不!从包含上述定义的文件中的注释:
A generic implementation of type cast. For this implementation to
work, we need to import it at a higher level in the module hierarchy
than all clients of the class. Otherwise, type simplification will
inline TypeCast x y, which implies compile-time unification of x and y.
也就是说,类型简化器(其作用是删除类型同义词和常量类约束的使用)将在x ~ y
中看到TypeCast x x
,因为这是唯一匹配的实例,但仅在某些情况下。由于在不同情况下行为不同的代码是“非常糟糕”,因此HList的作者有第二个实现,即原始帖子中的实现。让我们来看看:
class TypeCast a b | a -> b, b -> a
class TypeCast' t a b | t a -> b, t b -> a
class TypeCast'' t a b | t a -> b, t b -> a
instance TypeCast' () a b => TypeCast a b
instance TypeCast'' t a b => TypeCast' t a b
instance TypeCast'' () a a
在这种情况下,TypeCast x y
可以从不简化而不查看类约束(简化器不会这样做!);没有实例头,这可能意味着x ~ y
。
但是,我们仍然需要在某个时间点断言x ~ y
- 所以我们用更多的类来做!
我们在a ~ b
中了解TypeCast a b
的唯一方法是TypeCast () a b
隐含a ~ b
。仅当TypeCast'' () a b
暗示a ~ b
时才会出现这种情况。
我不能不讲你的全部故事;我不知道为什么
instance TypeCast' () a b => TypeCast a b
instance TypeCast' () a a
不够(它有效 - 我不知道为什么它不会被使用)。我怀疑它与错误消息有关。我相信你可以找到Oleg并问他!
答案 1 :(得分:2)
HList文件发表在2004年Haskell研讨会的会议记录中,因此可从ACM DL和其他档案中获取。唉,发布版本中的解释缩写为缺乏空间。有关完整说明,请参阅作为技术报告发布的论文的扩展版本,该报告可在以下网址获得 http://okmij.org/ftp/Haskell/HList-ext.pdf(自从Ralf很久以前离开CWI以来,CWI链接确实不再有效。)请参阅TR中的附录D以解释TypeCast。
在最新的GHC中,您可以编写TypeCast x y
而不是x ~ y
约束。没有相应的typeCast方法:不再需要它。当您编写x ~ y
约束时,GHC会自动和幕后合成类似于类型广播(称为强制)的内容。
直接在电子邮件中询问我的问题通常可以更快地回复。