此代码:
{-# LANGUAGE TypeFamilies #-}
module Study where
class C a where
type T a :: *
pred :: T a -> Bool
- 给出了这个错误:
.../Study.hs:7:5: error:
• Couldn't match type ‘T a’ with ‘T a0’
Expected type: T a -> Bool
Actual type: T a0 -> Bool
NB: ‘T’ is a type function, and may not be injective
The type variable ‘a0’ is ambiguous
• In the ambiguity check for ‘Study.pred’
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
When checking the class method:
Study.pred :: forall a. A a => T a -> Bool
In the class declaration for ‘A’
|
7 | pred :: T a -> Bool
| ^^^^^^^^^^^^^^^^^^^
将type
个关键字替换为data
次修复。
pred
?答案 0 :(得分:6)
instance C Int where
type T Int = ()
pred () = False
instance C Char where
type T Char = ()
pred () = True
所以现在你有两个pred
的定义。因为类型族只分配类型同义词,所以这两个定义具有签名
pred :: () -> Bool
和
pred :: () -> Bool
嗯,看起来很相似,不是吗?类型检查器无法区分它们。那么,是什么
pred ()
应该是? True
或False
?
要解决此问题,您需要一些明确的方法来提供某些用例中特定pred
应属于哪个实例的信息。正如您自己发现的那样,一种方法是更改为关联的data
系列:data T Int = TInt
和data T Char = TChar
将是两种可区分的新类型,而不是同义词类型,无法确保它们实际上是不同的。即数据族总是单射的;类型家庭有时候不是。在没有其他提示的情况下,编译器假定 no 类型族是单射的。
您可以使用其他语言扩展名声明一个类型系列:
{-# LANGUAGE TypeFamilyDependencies #-}
class C a where
type T a = (r :: *) | r -> a
pred :: T a -> a
=
只是将r
的{{1}} esult绑定到一个名称,因此它在injectivity注释T
的范围内,其内容类似于函数依赖: r -> a
的{{1}} esult足以确定r
个参数。以上情况现在是非法的; T
和a
一起违反了注入性。只有一个本身是可以接受的。
或者,您可以按照编译器的提示进行操作; type T Int = ()
使原始代码编译。但是,您需要type T Char = ()
来解析使用站点上的实例:
-XAllowAmbiguousTypes