为什么"(1 + 1.0)"具有类型"分数a =>一个"而不是" Num a =>一个&#34 ;?

时间:2015-02-07 16:43:26

标签: haskell

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结合生成最终{{}},对我来说似乎更自然1}}(虽然这也很奇怪,因为我们会丢失从Num1.0的信息。

3 个答案:

答案 0 :(得分:9)

每个小数都是Num,但不是每个Num都是Fractional。因此,如果我们有Num 1,那么它可能是Fractional(因为某些NumFractional s),或者它不是。但是,1.0只能是Fractional,它绝对不能是Num之类的其他Integer

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


以下是一个类似示例的示例,该示例仅涉及用户定义的类型类而不是Num。也许这会让事情变得更清楚:

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

答案 1 :(得分:3)

类型类与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

答案 2 :(得分:2)

  • 数字1(技术上代表应用于fromIntegerInteger的{​​{1}})属于类1中的所有类型。
  • 班级Num中的所有类型也属于班级Fractional

人体工程学,

号码Num属于班级1中的所有类型。