9

假设我要(+)在 Strings 上定义,但不是通过给出Num String.

为什么 Haskell 现在隐藏了Nums(+)函数?毕竟我提供的功能:

(+) :: String -> String -> String

可以通过编译器与 Prelude 的(+). 为什么两个函数不能存在于同一个命名空间中,但具有不同的、不重叠的类型签名?

只要代码中没有调用函数,Haskell 就会关心是否存在歧义。然后使用参数调用函数将确定类型,以便可以选择适当的实现。

当然,一旦有实例Num String,实际上就会发生冲突,因为此时 Haskell 无法根据参数类型决定选择哪个实现,如果函数被实际调用。
在这种情况下,应该引发错误。

这不会允许函数重载而没有陷阱/歧义吗?

注意:我不是在谈论动态绑定。

4

2 回答 2

19

Haskell 根本不支持函数重载(通过类型类除外)。原因之一是函数重载不适用于类型推断。如果你有类似的代码,f x y = x + yHaskell 怎么知道是数字还是字符串,即类型应该是还是?xyff :: Num a => a -> a -> af :: String -> String -> String

PS:这与您的问题并不真正相关,但是如果您假设一个开放的世界,类型并不是严格不重叠的,即在某个模块中的某个地方可能有一个实例Num String,导入时会破坏​​您的代码. 因此,Haskell 永远不会根据给定类型没有给定类型类的实例这一事实做出任何决定。当然,即使不涉及类型类,函数定义也会隐藏具有相同名称的其他函数定义,所以正如我所说:与您的问题并不真正相关。


关于为什么有必要在定义站点知道函数的类型而不是在调用站点推断:首先,函数的调用站点可能与函数定义位于不同的模块中(或在多个不同的模块),所以如果我们必须查看调用站点来推断函数的类型,我们就必须跨模块边界执行类型检查。也就是说,在对模块进行类型检查时,我们还必须遍历导入该模块的所有模块,因此在最坏的情况下,每次更改单个模块时我们都必须重新编译所有模块。这将极大地复杂化并减慢编译过程。更重要的是,它会使编译库变得不可能,因为它'

于 2013-02-06T10:09:24.997 回答
7

只要函数没有被调用

在某些时候,使用该功能

不不不。在 Haskell 中,您不会想到“之前”或“您所做的那一刻......”,而是一劳永逸地定义事物。这在变量的运行时行为中最为明显,但也转化为函数签名和类实例。这样,您就不必考虑编译顺序的所有繁琐问题,并且可以避免多种方式,例如 C++ 模板/重载经常由于程序中的一个微小变化而严重中断。

另外,我认为您不太了解 Hindley-Milner 的工作原理。

在你调用函数之前,你知道参数的类型,它不需要知道。

好吧,您通常知道参数的类型!有时可能会显式给出,但通常是从另一个参数或返回类型推导出来的。例如,在

map (+3) [5,6,7]

编译器不知道数字文字的类型,它只知道它们是数字。这样,您可以根据自己的喜好评估结果,这允许您在其他语言中只能梦想的东西,例如符号类型,其中

> map (+3) [5,6,7] :: SymbolicNum
[SymbolicPlus 5 3, SymbolicPlus 6 3, SymbolicPlus 7 3]
于 2013-02-06T11:20:27.287 回答