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

vdzxcuhz  于 2024-01-08  发布在  其他
关注(0)|答案(3)|浏览(86)

自:

  • 1的类型为Num a => a
  • 1.0的类型为Fractional a => a

为什么1 + 1.0的类型是Fractional a => a
这对我来说似乎很奇怪,因为1不是分数。只有1.0是分数。那么,1是如何变成分数并与1.0结合形成分数的呢?
因为只有Num+运算符,所以在我看来,如果1.0变成Num,与1结合产生最终的Num,这似乎更自然,尽管这也很奇怪,因为我们会丢失从1.01的信息。

wyyhbhjk

wyyhbhjk1#

每个分数都是Num,但不是每个Num都是Fractional。所以如果我们有一个像1这样的Num,它可能是Fractional(因为有些Num s是Fractional s)或者它不可能是。但是1.0只能是Fractional,它肯定不可能是像Integer这样的其他Num
因此,当编译器看到您将1添加到Fractional时,它意识到在这种情况下1也必须是Fractional-否则不允许将其添加到Fractional
这里有一个类似的例子,只涉及用户定义的类型类,而不是Num s。也许这会让你更清楚:

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


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

nfs0ujit

nfs0ujit2#

类型类非常像OO类,这一点怎么强调都不过分。
特别是,“如果1.0变成Num“没有任何意义。Num是一个类型类,而不是一个类型,所以没有什么可以“变成Num“。事实上,在Haskell中没有什么可以变成其他东西--所有东西都有一个具体的类型,那是固定的。
现在你会问,多态函数是如何工作的?好吧,它被称为 * 参数 * 多态是有原因的:看似“任意类型a“实际上是一个 * 类型参数 *。像函数参数一样,这些不是变量,因为它们可以在事后更改其值,但是它们是可变的,因为允许函数的调用者为a选择任何特定的“类型值”--只要它满足类型-类约束。
所以从某种意义上说,字面量1是一个函数:它接受一个 * 类型参数 * a,并返回一个值1 :: a。它需要的是a在类Num中。
然后我们有(+)1.0,它们都需要相同的a参数。(+)再次需要Num,没有什么新的;但是1.0需要Fractional a。所以总的来说,1 + 1.0是一个接受类型参数a的“三个副本”的函数,并且需要

  • Num a
  • Num a
  • Fractional a-也需要Num a,因为NumFractional的超类。

如果我们真的要把类型写成

(1 + 1.0) :: (Num a, Num a, Fractional a, Num a) => a

字符串
所以我们可以去掉多余的约束,只留下Fractional a,这意味着所有其他的约束。我们不能只留下Num中的一个约束,因为这不意味着Fractional

niknxzdl

niknxzdl3#

  • 数字1(技术上代表应用于Integer1fromInteger)属于Num类中的所有类型。
  • Fractional类中的所有类型也属于Num类。

因此,
数字1属于Fractional类中的所有类型。

相关问题