自:
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.0到1的信息。
1 + 1.0
Num
+
wyyhbhjk1#
每个分数都是Num,但不是每个Num都是Fractional。所以如果我们有一个像1这样的Num,它可能是Fractional(因为有些Num s是Fractional s)或者它不可能是。但是1.0只能是Fractional,它肯定不可能是像Integer这样的其他Num。因此,当编译器看到您将1添加到Fractional时,它意识到在这种情况下1也必须是Fractional-否则不允许将其添加到Fractional。这里有一个类似的例子,只涉及用户定义的类型类,而不是Num s。也许这会让你更清楚:
Fractional
Integer
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:
foo
bar
combine foo bar
型这大致相当于在示例中添加1(类型为Num a => a)和1.0(类型为Fractional a => a)。就像您的示例一样,这工作正常,类型为Bar a => a。
Bar a => a
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的“三个副本”的函数,并且需要
a
1 :: a
(+)
Fractional a
Num a
如果我们真的要把类型写成
(1 + 1.0) :: (Num a, Num a, Fractional a, Num a) => a
字符串所以我们可以去掉多余的约束,只留下Fractional a,这意味着所有其他的约束。我们不能只留下Num中的一个约束,因为这不意味着Fractional。
niknxzdl3#
fromInteger
因此,数字1属于Fractional类中的所有类型。
3条答案
按热度按时间wyyhbhjk1#
每个分数都是
Num
,但不是每个Num
都是Fractional
。所以如果我们有一个像1
这样的Num
,它可能是Fractional
(因为有些Num
s是Fractional
s)或者它不可能是。但是1.0
只能是Fractional
,它肯定不可能是像Integer
这样的其他Num
。因此,当编译器看到您将1添加到
Fractional
时,它意识到在这种情况下1
也必须是Fractional
-否则不允许将其添加到Fractional
。这里有一个类似的例子,只涉及用户定义的类型类,而不是
Num
s。也许这会让你更清楚:字符串
通过上面的类型类,我们现在有以下方法:
型
所以现在让我们尝试像这样组合合并
foo
和bar
:型
这大致相当于在示例中添加
1
(类型为Num a => a
)和1.0
(类型为Fractional a => a
)。就像您的示例一样,这工作正常,类型为Bar a => a
。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
,因为Num
是Fractional
的超类。如果我们真的要把类型写成
字符串
所以我们可以去掉多余的约束,只留下
Fractional a
,这意味着所有其他的约束。我们不能只留下Num
中的一个约束,因为这不意味着Fractional
。niknxzdl3#
1
(技术上代表应用于Integer
值1
的fromInteger
)属于Num
类中的所有类型。Fractional
类中的所有类型也属于Num
类。因此,
数字
1
属于Fractional
类中的所有类型。