为什么“(1 + 1.0)”的类型是 "Fractional a => a" 而不是 "Num a => a"?

Why does "(1 + 1.0)" have the type "Fractional a => a" and not "Num a => a"?

1 具有类型 Num a => a

1.0 的类型为 Fractional a => a

为什么 1+1.0 的类型是 Fractional a => a

这对我来说似乎很奇怪,因为 1 不是小数。只有 1.0 是小数。那么1是怎么变成小数并和1.0组合成小数的呢?

因为只有 Num 有 + 运算符,所以如果 1.0 变成 Num1 结合产生最终结果对我来说似乎更自然Num(尽管这也很奇怪,因为我们会丢失从 1.01 的信息)。

每个分数都是 Num,但并非每个 Num 都是 Fractional。因此,如果我们有一个像 1 这样的 Num,它可能是 Fractional(因为有些 NumFractional),也可能不是。但是1.0只能是Fractional,绝对不能是其他的Num,比如Integer.

因此,当编译器看到您将 1 添加到 Fractional 时,它意识到 1 在这种情况下也必须是 Fractional - 否则您不会允许将其添加到 Fractional.


这里有一个类似的例子,只涉及用户定义类型 类 而不是 Nums。也许这会让你更清楚:

class Foo a where
  foo :: a

class Foo a => Bar a where
  bar :: a
  combine :: a -> a -> a

通过上面的类型类我们现在有以下方法:

foo :: Foo a => a
bar :: Bar a => a
combine :: Bar a => a -> a -> a

所以现在让我们尝试像这样组合 foobar

combine foo bar

这大致相当于您尝试在示例中添加 1Num a => a 类型)和 1.0Fractional a => a 类型)。就像您的示例一样,它工作正常并且类型为 Bar a => a.

  • 数字1(技术上代表fromInteger应用于Integer1)属于class[=14]中的所有类型=].
  • classFractional中的所有类型也属于classNum

因此,

编号1属于classFractional中的所有类型。

类型class非常不像面向对象class,这一点怎么强调也不为过。

特别是,“如果 1.0 变成 Num” 没有任何意义。 Num 是一种类型 class,而不是一种类型,因此任何东西都不能“变成 Num”。事实上,在 Haskell 中,没有任何东西会变成其他东西——一切都有一个具体的类型,它是固定的。

现在你问,多态函数是如何工作的?嗯,它被称为 参数 多态是有原因的:看起来是“任意类型 a” 实际上是 类型参数 。就像函数参数一样,从它们可以在事后更改其值的意义上说,它们不是变量,但从允许函数调用者为 a – 只要它满足类型-class 约束。

所以在某种意义上,文字 1 是一个函数:它接受一个 类型的参数 a 和 returns 一个值1 :: a。它要求的是 a 在 class Num.

然后我们有 (+)1.0,它们都需要相同的 a 参数。 (+) 再次需要 Num,没什么新鲜的;但是 1.0 需要 Fractional a。所以总而言之,1 + 1.0 是一个函数,它接受类型参数 a 的“三个副本”,并且需要

  • Num a
  • Num a
  • Fractional a – 这也需要 Num a 因为 NumFractional.
  • 的超级 class

如果我们真的必须把类型写成

,那就太尴尬了
(1 + 1.0) :: (Num a, Num a, Fractional a, Num a) => a

所以允许省略多余的约束,只留下Fractional a,这意味着其余所有。我们不能做的是只保留 Num 约束之一,因为那并不意味着 Fractional.