类型奥秘。为什么这段代码可以编译?
Type mysteries. Why does this piece of code compile?
代码
default ()
h :: Bool
h = 1.0 == 1.0 --Error. Ambiguity.
不编译。这是意料之中的,因为存在歧义。它可以是 Float
或 Double
,Haskell 不知道我们想要哪一个。
但是代码
default ()
foo :: (Fractional a, Eq a) => a -> Bool
foo x = x == 1.0
编译成功。我不完全明白为什么。为什么这也不是模棱两可的?
我感觉这是因为每当我们调用 foo
时,我们保证选择了一个具体类型来代替 a
,即我们保证有固定的 a
在编译时转换为 Float
或 Double
(或者我们的自定义类型同时具有 Fractional
和 Eq
的实例),因此没有歧义。
但这只是感觉,不知道是不是100%准确。
Section 4.3.4 of Haskell Report 中给出了此上下文中歧义的定义:
We say that an expression e
has an ambiguous type if, in its type forall us. cx => t
, there is a type variable u
in us
that occurs in cx
but not in t
. Such types are invalid.
在foo :: (Fractional a, Eq a) => a -> Bool
中没有这个变量(因为a
出现在a -> Bool
中)。 1.0 == 1.0
里面的类型其实是(Fractional a, Eq a) => Bool
,所以才会有这么一个变量。
原因这种歧义是一个错误是因为编译器没有办法在变量a
的不同实例化之间进行选择,结果是该程序可以取决于它选择的是哪一个。在这种情况下,对于任何 合理的 实例化,1.0 == 1.0
应该始终是 True
,但是 1) 编译器不知道这一点; 2)并非所有实例化都是合理的。
第二段代码编译通过,因为
foo :: (Fractional a, Eq a) => a -> Bool
foo x = x == 1.0
(==) :: Eq a => a -> a
所以编译器会知道浮点数文字 1.0 :: Rational a => a
与 x
具有相同的类型,它有一个 Eq
实例,即 "meaning" 的 ==
在这个函数中对于每次调用都是确定的。
而在第一段代码中,文字可以是任何类型(Rational a, Eq a) => a
,所以==
的"meaning"是有歧义的,h的值可以是True或false,取决于文字的类型,没有提供。
这是另一种概念性的表达方式:
这两种情况都有内在的歧义,但第一种是死胡同——编译器没有希望推断出解决歧义的方法。
第二种情况也有歧义,但歧义是公开的——无论谁调用歧义代码,都必须解决歧义,否则 delegate/bubble 它会进一步向上调用堆栈。
仅此而已 — 多态性是受控的歧义。第一种情况是不受控制的歧义,所以它没有用(即 polymorphism-related)歧义。所以它会被拒绝,直到它被注释为明确为止。
代码
default ()
h :: Bool
h = 1.0 == 1.0 --Error. Ambiguity.
不编译。这是意料之中的,因为存在歧义。它可以是 Float
或 Double
,Haskell 不知道我们想要哪一个。
但是代码
default ()
foo :: (Fractional a, Eq a) => a -> Bool
foo x = x == 1.0
编译成功。我不完全明白为什么。为什么这也不是模棱两可的?
我感觉这是因为每当我们调用 foo
时,我们保证选择了一个具体类型来代替 a
,即我们保证有固定的 a
在编译时转换为 Float
或 Double
(或者我们的自定义类型同时具有 Fractional
和 Eq
的实例),因此没有歧义。
但这只是感觉,不知道是不是100%准确。
Section 4.3.4 of Haskell Report 中给出了此上下文中歧义的定义:
We say that an expression
e
has an ambiguous type if, in its typeforall us. cx => t
, there is a type variableu
inus
that occurs incx
but not int
. Such types are invalid.
在foo :: (Fractional a, Eq a) => a -> Bool
中没有这个变量(因为a
出现在a -> Bool
中)。 1.0 == 1.0
里面的类型其实是(Fractional a, Eq a) => Bool
,所以才会有这么一个变量。
原因这种歧义是一个错误是因为编译器没有办法在变量a
的不同实例化之间进行选择,结果是该程序可以取决于它选择的是哪一个。在这种情况下,对于任何 合理的 实例化,1.0 == 1.0
应该始终是 True
,但是 1) 编译器不知道这一点; 2)并非所有实例化都是合理的。
第二段代码编译通过,因为
foo :: (Fractional a, Eq a) => a -> Bool
foo x = x == 1.0
(==) :: Eq a => a -> a
所以编译器会知道浮点数文字 1.0 :: Rational a => a
与 x
具有相同的类型,它有一个 Eq
实例,即 "meaning" 的 ==
在这个函数中对于每次调用都是确定的。
而在第一段代码中,文字可以是任何类型(Rational a, Eq a) => a
,所以==
的"meaning"是有歧义的,h的值可以是True或false,取决于文字的类型,没有提供。
这是另一种概念性的表达方式:
这两种情况都有内在的歧义,但第一种是死胡同——编译器没有希望推断出解决歧义的方法。
第二种情况也有歧义,但歧义是公开的——无论谁调用歧义代码,都必须解决歧义,否则 delegate/bubble 它会进一步向上调用堆栈。
仅此而已 — 多态性是受控的歧义。第一种情况是不受控制的歧义,所以它没有用(即 polymorphism-related)歧义。所以它会被拒绝,直到它被注释为明确为止。