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}}(虽然这也很奇怪,因为我们会丢失从Num
到1.0
的信息。
答案 0 :(得分:9)
每个小数都是Num
,但不是每个Num
都是Fractional
。因此,如果我们有Num
1
,那么它可能是Fractional
(因为某些Num
是Fractional
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
现在让我们尝试将foo
和bar
结合起来:
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
,因为Num
是Fractional
的超类。如果我们实际上必须将类型写为
,那将是非常尴尬的(1 + 1.0) :: (Num a, Num a, Fractional a, Num a) => a
因此可以省略冗余约束,只留下Fractional a
,这意味着所有其余约束。我们不能做的只是留下Num
个约束之一,因为这并不意味着Fractional
。
答案 2 :(得分:2)
1
(技术上代表应用于fromInteger
值Integer
的{{1}})属于类1
中的所有类型。Num
中的所有类型也属于班级Fractional
。 人体工程学,
号码Num
属于班级1
中的所有类型。