在 Haskell 中,独立表达式3 + 5
表示fromInteger (3 :: Integer) + fromInteger (5 :: Integer)
。在 GHCi 中,它被评估为fromInteger 8
类型Num a => a
。很明显,Haskell 可以计算(3 :: Integer) + (5 :: Integer)
,但评估原始多态表达式似乎需要 形式的某种规则,而我在 Prelude 中没有看到这样的规则。在 GHCi 中fromInteger x + fromInteger y = fromInteger (x + y)
, 的评估究竟是如何完成的?3 + 5
默认设置在这里起作用吗?似乎没有,因为如果我default ()
在提示符下输入,则3 + 5
仍然计算正确,而例如,\(x :: Float) -> x^2
无法进行类型检查。如果我使用恢复默认设置default (Integer, Double)
,则\(x :: Float) -> x^2
类型检查将按预期进行。
是的,默认就是如此运作的。
+
具有类型Num a => a -> a -> a
。因此3 + 5
确实可以用来编写类型 的值的表达式Num a => a
。但是,如果我们将其保留为该类型,则该值实际上永远不会被求值!+
在确定要Num
使用哪个实例之前,我们无法确定要运行哪个代码来求值表达式,因此无法3 + 5
在多态类型 处求值。Num a => a
实际上,如果您实际创建了这样的值,它在内部表示为一个函数,其中参数是标识Num
实例的字典,并且主体将使用字典的定义+
(以及 来fromInteger
求值文字3
和5
,事实上;它们也不能被多态地求值)来求值表达式。当您
3 + 5
在 GHCi 中输入内容时,它似乎已经多态地评估了您的表达式,因为它将(使用默认设置)打印8
。但实际发生的情况是,表达式创建一个不评估加法的值(内部表示为一个函数),然后当 GHCi 尝试获取print
您最后输入的值时,它无法在没有实际评估它的情况下执行此操作,因此它将默认应用于期间值的使用print
。默认(使用默认设置)会出现Integer
,因此它将 的字典传递Num Integer
给表示 的函数3 + 5
,然后该函数可以使用Integer
加法来生成Integer
值8
。如果在要求其为的上下文中使用相同的表达式,
Double
则表示的底层函数3 + 5
将被传递Num Double
实例字典,然后使用Double
加法来产生Double
值8.0
。加法的结果实际上永远不会表示为具有类型的具体多态值
Num a => a
。只有在选择了具体类型并传入实例字典后,它才会被实际评估。如果您尚未确定具体类型,那么如果您需要实际评估表达式(例如能够获得print
具体值),您将收到模糊类型变量错误,除非默认为您选择具体类型。