为什么Haskell不允许所有类型的函数定义?


这是一个功能,而且实际上是非常重要的。 它归结为在编程语言理论中被称为参数性的属性。 粗略地说,这意味着评估不应该依赖编译时变量的类型。 在静态不知道具体类型的情况下,您无法查看值。

为什么这很好? 它给了程序更强大的不变性。 例如,你只从类型知道a -> a必须是身份函数(或分歧)。 类似的“自由定理”适用于许多其他多态函数。 参数化也是更高级的基于类型的抽象技术的基础。 例如,类型ST sa在Haskell(状态单子),以及相应的类型runST功能,依靠s是参数化的。 这可以确保运行函数无法混淆状态的内部表示。

这对高效实施也很重要。 程序不必在运行时传递昂贵的类型信息(类型擦除),编译器可以为不同类型选择重叠表示。 作为后者的一个例子,0和False以及()和[]在运行时都用0表示。 如果像你这样的功能被允许,这是不可能的。


Haskell享有一种称为“类型擦除”的实现策略:类型没有计算重要性,因此您发出的代码不需要跟踪它们。 这对性能有很大的帮助。

您为此性能优势付出的代价是类型不具有计算意义:函数不能根据传递参数的类型来改变其行为。 如果你允许类似的东西

f () = "foo"
f [] = "bar"

那么这个属性就不会是真的: f的行为的确会取决于它的第一个参数的类型。

当然有语言允许这种事情,特别是在依赖类型的语言中,类型通常不能被擦除。


对于函数a -> Integer ,只有一个允许的行为 - 返回一个常量整数。 为什么? 因为你不知道什么样的a是。 在没有指定约束的情况下,它可能完全是任何东西,并且因为Haskell是静态类型的,所以需要在编译时解决与类型相关的所有问题。 在运行时,类型信息不再存在,因此无法查询 - 所使用的功能的所有选择都已经完成。

最接近Haskell允许这种行为的是类型类的使用 - 如果你用一个函数创建了一个名为Foo的类型类:

class Foo a where
    foo :: a -> Integer

然后你可以为不同的类型定义它的实例

instance Foo [a] where
    foo [] = 0
    foo (x:xs) = 1 + foo xs

instance Foo Float where
    foo 5.2 = 10
    foo _ = 100

那么只要你可以保证一些参数x是一个Foo你就可以在它上面调用foo 。 尽管如此,你仍然需要 - 你不能写一个函数

bar :: a -> Integer
bar x = 1 + foo x

因为编译器不知道aFoo一个实例。 你必须告诉它,或者省去类型签名,并让它自行解决。

bar :: Foo a => a -> Integer
bar x = 1 + foo x

Haskell只能使用编译器提供的有关某种类型的信息。 这可能听起来有些限制,但实际上,类型类和参数多态性非常强大,我从不错过动态类型。 事实上,我通常会发现动态打字烦人,因为我从不完全确定实际是什么。

链接地址: http://www.djcxy.com/p/43025.html

上一篇: Why is function definition for all types at once not allowed in Haskell?

下一篇: Haskell: What is Weak Head Normal Form?