这就是我的困境: 当编译Haskell程序时,生成可以在物理CPU上执行的二进制可执行机器代码。当解释Haskell程序时,物理CPU对来自内存位置的数据执行操作。执行实际上发生在一段时间内,其长度取决于CPU速度。
由于数据已由编译器放入内存中,该编译器保证每个变量都是强类型的,这是否意味着Haskell实际上是否具有运行时类型?
澄清: 按"运行时类型"我的意思是:在语言编译时由语言编译器可访问/识别的物理处理器上执行程序时变量的类型(在类型 - 理论意义上)。
答案 0 :(得分:10)
Haskell的语言功能旨在支持完整的类型擦除。我们的想法是输入规则保证通过类型检查的代码具有以下两个属性 1 :
第二财产继续扩张。显然,fromJust
和head
等部分功能实际上并不处理任何可能的值;如果给出错误的值,它们会在运行时爆炸。但在检查了他们所依赖的假设之后,他们以“定义明确”的方式爆炸。编写试图访问可能没有该子结构的值的子结构的代码是不可能的,这可能导致分段错误或解释随机内存片段,就好像它们是不同类型的数据一样。
因此,在类型检查成功后,无需在编译的程序中存储任何类型信息。我出租车只是把类型扔掉。我已经证明,如果我生成的代码盲目地对数据执行操作,假设它具有所需的形状,那么实际上什么都不会出错。
示例时间!
data Foo = Foo Int String
data Bar = Bar Int String
Foo和Bar的值可能会在内存中以相同的方式表示 2 ;一旦您知道所有值的类型都是正确的,那么定义这些值所需的所有信息在每种情况下都是Int
和String
。如果您要查看正在运行的Haskell程序的内存转储,您将无法判断包含对Int
和String
的引用的给定内存对象是否为{{1} }或Foo
(或实际上是Bar
,或任何其他单个构造函数类型,其中包含两个分别为(Int, String)
和Int
的字段。
所以不,Haskell类型在运行时不存在任何形式。
1 您当然可以使用不安全的功能来破坏这些属性,例如String
;我在这里谈论“正常”的Haskell代码。
2或者他们可能没有。 Haskell-the-language根本不保证运行时的表示,并且它完全有可能以不同的顺序存储字段,或者做一些可以区分两者的东西。但我会假设在没有任何理由的情况下,它会将这两种类型视为相同。
答案 1 :(得分:7)
"运行时类型"在任何编译程序中使用的,无论源语言如何,都是目标处理器本机支持的类型:通常是整数(有符号和无符号),指针(实际上只是整数的特殊用法),以及浮动 - 点数(通常为IEEE754)。编译器使用这些基本的硬件支持类型将源程序中的所有操作转换为等效的一系列操作。
更复杂的数据类型(例如Haskell列表)在运行时表示为从基本类型构建的数据结构。例如,列表中的每个节点可以由一对指针表示:一个指针由该节点保持的值(或用于计算它的thunk),一个指向下一个节点(或thunk)。编译器的静态类型检查允许它确保每个运行时数据结构只能由能够正确处理它的代码访问。例如,为列表节点保存一对指针的内存区域不会被错误地视为计算整数的thunk,因为编译器只允许将列表传递给期望列表参数的函数
答案 2 :(得分:6)
运行时类型信息通常在OOP语言实现中找到,它带有每个对象的“类型标记”。例如,拥有这样的信息可以编写诸如
之类的代码void foo(Object o) {
if (o instanceof SomeClass) {
...
}
}
这是一种运行时类型检查。 “类型标记”通常是免费的,因为每个对象都需要一个指向虚方法表的指针,并且它只能识别对象的运行时类型。
但是,在Haskell中,不需要这样的标记,也不需要指向VMT的指针。该语言的设计没有任何instanceof
运算符,因此实现不必在运行时提供任何类型标记。这也导致了更好的基础理论,因为我们获得了代码的参数保证,也被称为“免费定理!”。例如以下函数
f :: [a] -> [a]
f = .... -- whatever
不可以实现f [1,2] = [2,3]
。这是因为,有可能无法为3
生成f
。直觉是f
必须产生a
,并且它无法在运行时检查a=Int
(没有类型标签),因此f
的输出只能包括在其输入中找到的元素。这种保证只来自上面的输入,甚至没有关心f
实际实现的方式。
如果你真的想要一个instanceof
等价物,你可以使用Typeable
:
f :: Typeable a => [a] -> [a]
f [] = []
f (x:xs) = case cast x of
Just (y :: Int) -> [2,3]
Nothing -> x:x:xs
如果是整数,这将在所有非空列表上返回[2,3]
。在运行时,将传递类型标记,以便可以检查a=Int
。
答案 3 :(得分:1)
没有。 Haskell的一个基本属性是newtype
声明,如
newtype MyInt = MyInt Int
没有运行时开销,这意味着构造函数MyInt
可以逐字地返回它传递的相同参数(无论是什么),编译器会将其视为不同的类型。 (如果你查看编译后的代码,在优化之后你甚至不会看到任何实现MyInt
函数调用的指令。)这意味着Haskell中任何一个'de-factor'运行时类型的对象只会被定义为newtype
与其实现之间的等价。