在Haskell中键入擦除?

Liu*_*x31 7 haskell types

当我看到这段时,我正在阅读关于Haskell的讲义(http://www.seas.upenn.edu/~cis194/lectures/02-lists.html):

这种"不关心"是参数多态性中的"参数"意味着什么.所有Haskell函数必须在其类型参数中是参数化的; 功能必须不关心或根据这些参数的选择做出决定.当a是Int时,函数不能做一件事,当a是Bool时,函数不能做.Haskell根本没有提供编写这种操作的工具.语言的这种属性称为参数.

参数化有许多深刻而深刻的后果.一个后果是称为类型擦除.因为正在运行的Haskell程序永远不能根据类型信息做出决策,所以在编译期间可以删除所有类型信息.尽管在编写Haskell代码时类型有多重要,但在运行Haskell代码时它们完全不相关.与其他语言(如Python)相比,此属性为Haskell提供了巨大的速度提升,需要在运行时保留类型.(类型擦除并不是使Haskell更快的唯一因素,但Haskell有时的速度比Python快20倍.)

我不明白的是"所有Haskell函数"如何参数化?Haskell中的类型不是显式/静态的吗?另外,我真的不明白类型擦除如何改善编译时间运行时间?

对不起,如果这些问题非常基本,我是Haskell的新手.

编辑:

还有一个问题:为什么作者会说"尽管在编写Haskell代码时类型有多重要,但在运行Haskell代码时它们完全不相关"?

gde*_*ohn 13

我不明白的是"所有Haskell函数"如何参数化?

它没有说所有Haskell函数都是参数化的,它说:

所有Haskell函数必须在其类型参数中具有参数.

Haskell函数不需要任何类型参数.

还有一个问题:为什么作者会说"尽管在编写Haskell代码时类型有多重要,但在运行Haskell代码时它们完全不相关"?

与动态类型语言不同,您需要在运行时检查(例如)在尝试将它们添加到一起之前是两个数字时,运行的Haskell程序知道如果您尝试将它们添加到一起,那么它们必须是数字因为编译器事先确定了它.

Haskell中的类型不是显式/静态的吗?

通常可以推断出Haskell中的类型,在这种情况下,它们不需要是显式的.但你是对的,他们是静态的,这实际上是为什么它们在运行时无关紧要,因为静态意味着编译器确保所有东西都具有应该在程序执行之前应该具有的类型.


aug*_*tss 10

类型可以在Haskell中擦除,因为表达式的类型要么在编译时知道(比如True),要么它的类型在运行时无关紧要(比如[]).

虽然有一点需要注意,它假定所有值都有某种统一的表示.大多数Haskell实现都使用指针来处理所有内容,因此指针所指向的实际类型无关紧要(垃圾收集器除外),但您可以想象一个Haskell实现使用非均匀表示然后某些类型信息会必须保持.


chi*_*chi 6

其他人已经回答,但也许一些例子可以帮助.

例如,Python在运行时之前保留类型信息:

>>> def f(x):
...   if type(x)==type(0):
...     return (x+1,x)
...   else:
...     return (x,x)
... 
>>> f("hello")
('hello', 'hello')
>>> f(10)
(11, 10)
Run Code Online (Sandbox Code Playgroud)

给出的任何参数的上面的功能,x返回对(x,x),除了x为类型int.该函数在运行时测试该类型,如果x发现int它以特殊方式运行,则返回(x+1, x).

要实现上述目的,Python运行时必须跟踪类型.也就是说,当我们做的时候

>>> x = 5
Run Code Online (Sandbox Code Playgroud)

Python不能只存储5内存中的字节表示.它还需要使用类型标记来标记该表示int,以便在我们执行时type(x)可以恢复标记.

此外,在执行任何操作(如x+1Python)之前,需要检查类型标记以确保我们真正在使用ints.x例如str,如果是ing,Python将引发异常.

静态检查的语言(如Java)在运行时不需要进行此类检查.例如,当我们跑步时

SomeClass x = new SomeClass(42);
x.foo();
Run Code Online (Sandbox Code Playgroud)

编译器已经检查的确有一个方法foo用于x在编译时,所以没有必要再这样做.原则上,这可以改善性能.(实际上,JVM在类加载时执行一些运行时检查,但为了简单起见,我们忽略它们)

尽管如此,Java 必须像Python一样存储类型标签,因为它有type(-)类似的:

if (x instanceof SomeClass) { ...
Run Code Online (Sandbox Code Playgroud)

因此,Java允许编写可以在某些类型上"特殊"运行的函数.

// this is a "generic" function, using a type parameter A
<A> A foo(A x) {
   if (x instanceof B) {  // B is some arbitrary class
      B b = (B) x;
      return (A) new B(b.get()+1);
   } else {
      return x;
   }
}
Run Code Online (Sandbox Code Playgroud)

上面的函数foo()只返回它的参数,除了它的类型B,为其创建一个新对象.这是使用的结果 instanceof,它要求每个对象在运行时携带标记.

说实话,这样的标签已经存在,以便能够实现虚拟方法,因此不需要花费更多.然而,存在instanceof使得可能在类型上引起上述非均匀行为 - 某些类型可以以不同方式处理.

相反,Haskell没有这样的type/instanceof运算符.具有类型的参数Haskell函数

foo :: a -> (a,a)
Run Code Online (Sandbox Code Playgroud)

必须在所有类型中以相同的方式行事.没有办法引起一些"特殊"的行为.具体来说,foo x 必须返回(x,x),我们只需通过查看上面的类型注释就可以看到这一点.为了强调这一点,没有必要查看代码(!!)来证明这种属性.这是参数化从上面的类型确保的.