22

我有一个关于 GHCi 如何假设整数类型的问题。

我正在阅读 Yes-No 类型的 Learn you a Haskell 课程。

如果您想阅读整个内容,这里有一个链接。 http://learnyouahaskell.com/making-our-own-types-and-typeclasses#a-yes-no-typeclass

简而言之,本章展示了通过定义我自己的类,我可以创建一个适用于多种类型的函数。

本书用一个函数定义了 YesNo 类

yesno :: a -> Bool

Int作为 YesNo 类的实例

instance YesNo Int where
    yesno 0 = False
    yesno _ = True

当我在我的 GHCi 上加载它并输入

yesno 0

它返回错误。我认为这可能是因为 GHCi 无法判断 0 是否意味着IntIntegerDouble或其他类型Num。实际上,当我输入 yesno (0::Int) 时,它起作用了。

所以只是为了好玩,我Integer作为YesNo类的一个实例写了

instance YesNo Integer where
    yesno 0 = True
    yesno _ = False

(请注意,我翻转了 True 和 False)然后我再次输入

yesno 0

(没有任何类型声明)然后 GHCi 显示True.

此外,当我输入

yesno $ fromIntegral 0

它返回了True,这意味着 GHCi 认为的类型fromIntegral 0Integer

那么,这是否意味着当我在 GHCi 上键入一个整数时,它通常会假定它的值是Integer而不是?我很困惑,因为:t 0返回Num a => a

4

3 回答 3

26

它是类型默认值以及 ghci 的扩展默认规则。

整数文字是多态的,它们具有类型Num a => a(因为它们代表fromInteger literal)。但是当一个表达式需要被计算时——例如打印它的结果所必需的——这个表达式必须被赋予一个单态类型。

通过它自己,

yesno 0

对 施加两个约束Num aYesNo a0整个表达式将具有模棱两可的类型

yesno 0 :: (Num a, YesNo a) => Bool

(这是模棱两可的,因为约束中的类型变量无法从 右侧的类型访问=>)。

通常,歧义类型是类型错误,但是,在某些情况下,歧义可以通过使用默认类型实例化受约束的类型变量来解决。语言规范中的规则是类型变量可以被默认,如果

在发现不明确类型的情况下,不明确类型变量 ,v是可默认的,如果:

- `v` appears only in constraints of the form `C v`, where `C` is a class, and
- at least one of these classes is a numeric class, (that is, `Num` or a subclass of `Num`), and
- all of these classes are defined in the Prelude or a standard library (Figures 6.2–6.3 show the numeric classes, and Figure 6.1 shows the classes defined in the Prelude.)

约束(Num a, YesNo a)满足前两个要求,但不满足第三个要求。所以按照语言标准,它是不可默认的,应该是类型错误。

但是,ghci 使用扩展的默认规则,并且默认类型变量受 Prelude 或标准库中未定义的类约束。

然后它将在Num此处为约束选择默认值,除非显式默认声明在范围内,否则将是Integer,或者,如果Integer不满足约束,Double则尝试。

所以当你有一个instance YesNo Integer,ghci 可以成功地将类型变量默认aInteger. 但是没有这样的实例可用,默认失败,因为没有一个默认候选者有实例。

于 2013-05-29T21:37:43.877 回答
8

那么,这是否意味着当我在 GHCi 上输入一个整数时,它通常会假定它的值为 Integer?

是的。基本上,GHCi 将首先尝试Integer,然后如果失败,Double然后最后()解决模棱两可的类型约束。您可以在 GHC 用户指南中阅读有关其工作原理的详细信息

但是,请注意,在编译模块中,规则要严格一些。特别是,默认设置仅适用于标准类,因此除非您启用与ExtendedDefaultRulesGHCi 具有相同行为的扩展,否则您的示例将无法在编译模块中没有类型注释的情况下工作。

于 2013-05-29T21:34:17.170 回答
1

在第一种情况下尝试写:

Prelude> yesno (0 :: Int)
False
于 2013-06-03T19:03:34.123 回答