我有两个比较简单的类。MSet
:
{-# Langage MultiParamTypeClasses #-}
-- A generalization of G set to Magmas
class MSet a b where
(+>>) :: a -> b -> a
instance MSet Integer Integer where
(+>>) = (+)
-- (+>>) constrained to a Magma
(<<+>>) ::
( MSet a a
)
=> a -> a -> a
(<<+>>) = (+>>)
当我启动ghci并尝试测试这些时,我遇到了一个问题。
*Main> 1 <<+>> 2
3
*Main> 1 +>> 2
<interactive>:31:1: error:
• Could not deduce (MSet a b0)
from the context: (MSet a b, Num a, Num b)
bound by the inferred type for ‘it’:
forall a b. (MSet a b, Num a, Num b) => a
at <interactive>:31:1-7
The type variable ‘b0’ is ambiguous
• In the ambiguity check for the inferred type for ‘it’
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
When checking the inferred type
it :: forall a b. (MSet a b, Num a, Num b) => a
当 (+>>)
当它被限制在一个 Magma
却时时含糊不清。
现在我可以做了。
*Main> :set -XFlexibleContexts
*Main> 1 +>> (2 :: Integer)
3
但我不明白这是怎么回事 也不明白为什么这个注解会有帮助 我真的不明白类型检查器是如何消除 (<<+>>)
. 如果我再添加一个实例,比如说 Int
它继续发挥作用,即使它似乎是它应该是模糊的。1
是一个 Int
或 Integer
.
为什么其中一个会出错,另外两个不会?
本质上--你要求GHC解决的是 "什么是类型的 1
和 2
言下之意 1 +>> 2
而你的类型意味着答案是模棱两可的。
<<+>>
什么类型的 1 <<+>> 2
? 为什么?(Num a, MSet a a) => a
当然,因为GHC必须能够将字面值转化为数值(Num
)然后 +>>
他们(MSet
)的类型签名,以及 <<+>>
说,这两个字元将具有相同的类型。
当你要求GHCi打印出 1 <<+>> 2
? 它试图默认 a
到 Integer
,它的成功是因为 Num Integer
和 MSet Integer Integer
. 然后,它将评估类型缺省的表达式。
这就是 Int
实例并没有引入歧义--GHCi并没有试图推断要使用的具体实例,而是推断类型,然后默认变量,只留下实例检查。
+>>
什么是类型的 1 +>> 2
? 嗯... (Num a, Num b, MSet a b) => a
...,似乎是。显然,你需要 MSet
但不再有保证说 a
和 b
统一。更不幸的是。b
没有出现在术语的类型中--这就是类型歧义的来源。类型系统不知道选择哪种类型。b
来使用。
当你要求GHCi打印出 1 +>> 2
? 它先是推断术语的类型,然后得到上面的类型--现在它在尝试默认之前就遇到了类型推断错误。a
到 Integer
.
添加类型信息可以防止错误
> 1 +>> (2 :: Integer) -- fine
因为这些变化消除了《公约》的模糊性。b
. GHC不需要推断 b
,所以它不会引起推理错误。
奇怪的是,我并不完全理解其中的原因,在为 a
似乎也可以防止错误的发生
> (1 :: Integer) +>> 2 -- fine
> (1 +>> 2) :: Integer -- fine
虽然我怀疑这是另一个GHCi特有的技巧,默认为 b
到 Integer
在 (1 +>>) :: (Num b, MSet Integer b) => b -> Integer
. 不过,不要引用我的话。
可以使用以下方法消除类型歧义 FunctionalDependencies
class MSet a b | a -> b where
...
虽然这似乎不符合你的用例。这解决了推理问题,因为在 (Num a, Num b, MSet a b) => a
,知道 a
就可以推断出 b
基金会在 MSet
. 后来,当GHCi默认为 a
到 Integer
,它可以只查找类型的 b
.