以下的代码是否使用动态分派,因为它在C ++或Java中被理解?
据我所知,在最后一行,编译器不可能在编译时知道调用(==)的实现,但代码编译并产生正确的结果。 有人可以解释一下,这背后有什么样的实现(比如vptr)?
{-# LANGUAGE ExistentialQuantification #-}
data Value = A Int
data ForallFunc = forall a. Eq a => Forall (Value -> a)
unpackA (A int) = int
equalityTest :: Value -> Value -> ForallFunc -> Bool
equalityTest arg1 arg2 (Forall unpacker) =
let a1 = unpacker arg1
a2 = unpacker arg2 in
a1 == a2
答案 0 :(得分:7)
粗略地说,是的。
当您使用有界存在量化时,即当量化类型变量受某些约束C a => ...
限制时,GHC将在构造函数中存储一些指针,记住方法C a
的{{1}},以便稍后在该构造函数上进行模式匹配时可以访问它们。
通常使用单个指针,就像许多OOP实现中的vptr
到vtable
一样。编译器也可以直接存储指向方法的指针,避免间接。我认为这是由GHC完成的,因为类型类中只有一个方法。它也可以在极少数(比方说两种)方法时使用 - 这会使每个值的内存占用更大,但访问速度更快。
因此,编译代码很简单,好像约束被Eq
字典替换,列出了它的方法。那就是:
data EqDict a = EqDict
{ eq :: a->a->Bool
, new :: a->a->Bool }
data ForallFunc = forall a. Forall EqDict (Value -> a)
unpackA (A int) = int
equalityTest :: Value -> Value -> ForallFunc -> Bool
equalityTest arg1 arg2 (Forall eqDict unpacker) =
let a1 = unpacker arg1
a2 = unpacker arg2 in
eq eqDict a1 a2
当然,编译构造函数调用Forall someFunction
大致是通过为所需的==
,/=
函数提供其特定类型的实现来编译的,这些函数将被抽象出来。存在量化。
答案 1 :(得分:4)
是的,ghc使用一种动态调度形式。 Eq a
约束意味着ForallFunc
的表示将包含在运行时用于调用正确的相等函数的字典(对应于vtable)。