Will a value that has a type with class constraints actually be a function at run time?
想想著名的
假设为了避免单态限制,注释如下:
1 |
这似乎意味着,在运行时,列表值
问题是,您所知道的不同的haskell实现中如何处理这种情况。
------我觉得我必须再详细说明一点。考虑:
并假设在程序执行期间
1 | (fibsInteger !! 42) |
需要评估。在这种情况下,我希望随后的评估会发现,
然而,据我所见,多态的
because a typeclass usually introduces a new argument containing a
dictionary with the functions of that typeclass
似乎支持我的观点,像
如果是这样,那么像
这取决于实现。在GHC中,类型类是使用字典实现的。假设
1 2 3 |
然后将其编译为"字典"数据类型:
1 |
任何带有
1 2 |
那么让我们看看GHC是如何编译
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | $ cat Fibs.hs module Fibs where fibs :: Num a => [a] fibs = 0 : 1 : zipWith (+) fibs (tail fibs) $ ghc -c Fibs.hs -ddump-simpl ==================== Tidy Core ==================== Rec { Fibs.fibs [Occ=LoopBreaker] :: forall a_abu. GHC.Num.Num a_abu => [a_abu] [GblId, Arity=1] Fibs.fibs = \ (@ a_akv) ($dNum_akw :: GHC.Num.Num a_akv) -> GHC.Types.: @ a_akv (GHC.Num.fromInteger @ a_akv $dNum_akw (GHC.Integer.smallInteger 0)) (GHC.Types.: @ a_akv (GHC.Num.fromInteger @ a_akv $dNum_akw (GHC.Integer.smallInteger 1)) (GHC.List.zipWith @ a_akv @ a_akv @ a_akv (GHC.Num.+ @ a_akv $dNum_akw) (Fibs.fibs @ a_akv $dNum_akw) (GHC.List.tail @ a_akv (Fibs.fibs @ a_akv $dNum_akw)))) end Rec } |
如果你斜视一点,这基本上是
1 2 3 4 | fibs :: Num a -> [a] fibs num = fromInteger num 0 : fromInteger num 1 : zipWith (plus num) (fibs num) (tail (fibs num)) |
所以对于GHC来说,答案是肯定的。正如您所怀疑的,这可能会对性能产生重大影响,因为这会破坏此定义所依赖的
我们可以通过介绍分享自己来解决这个问题:
这样好多了。
1 2 3 4 5 6 7 |
但它仍然存在着相同的问题,即
这些令人惊讶的性能是可怕的单态限制存在的部分原因。
1忽略
多态性会带来额外的性能负担(我认为这是您要问的问题)。在托马斯对这个问题的回答中,使类型非多态性将运行时间从36秒缩短到11秒。
你的声明:
This seems to imply that at runtime, a list value fibs does not really exist, but rather a function that computes the list anew each time an element of fibs is picked?
我真的不知道你在这里是什么意思-你似乎意识到这是懒惰的。您可能会问haskell是将其视为"函数声明"还是"值声明"——您可以尝试使用模板haskell:
所以它是一个值声明(vald)。
函数总是涉及
1 | (a -> b) -> a -> b |
当然,它是一个懒惰的值,在实现级别上涉及到一个称为thunk的东西,但这在很大程度上与您的问题无关。thunk是一个实现细节。只是因为它是一个延迟计算的值,所以不会将其转换为函数。不要混淆评估和执行!C语言中的函数与Haskell中的函数不同。haskell使用了一个函数的真正数学概念,这与在机器级别执行策略完全无关。
首先,列表是无限的,因此在程序运行之前不可能生成整个列表。正如MatrixFrog已经指出的,
很长一段时间,我发布了一个关于codegolf.se问题的答案,其中包含了自己在C语言中实现thunk的方法。代码不是很好,列表中的内容与thunk本身没有很好的分离,但值得一看。