gpt4 book ai didi

haskell - NoMonomorphismRestriction 有助于保持共享?

转载 作者:行者123 更新时间:2023-12-04 03:22:33 25 4
gpt4 key购买 nike

我试图回答 another question当我偶然发现这种奇怪的行为时,关于多态性与共享。

在 GHCi 中,当我显式定义一个多态常量时,它没有得到任何共享,这是可以理解的:

> let fib :: Num a => [a]; fib = 1 : 1 : zipWith (+) fib (tail fib)
> fib !! 30
1346269
(5.63 secs, 604992600 bytes)

另一方面,如果我尝试通过省略类型签名并禁用单态限制来达到同样的效果,我的常量会突然被共享!
> :set -XNoMonomorphismRestriction
> let fib = 1 : 1 : zipWith (+) fib (tail fib)
> :t fib
fib :: Num a => [a]
> fib !! 50
20365011074
(0.00 secs, 2110136 bytes)

为什么?!

呃......当使用优化编译时,即使禁用了单态限制,它也很快。

最佳答案

通过给出明确的类型签名,您可以防止 GHC 对您的代码做出某些假设。我将展示一个示例(取自 question):

foo (x:y:_) = x == y
foo [_] = foo []
foo [] = False

根据 GHCi,这个函数的类型是 Eq a => [a] -> Bool ,如你所料。但是,如果您声明 foo使用此签名,您将收到“模糊类型变量”错误。

这个函数只能在没有类型签名的情况下工作的原因是因为类型检查在 GHC 中是如何工作的。如果省略类型签名, foo假定为单型 [a] -> Bool对于一些固定类型 a .一旦你完成了绑定(bind)组的输入,你就可以概括这些类型。这就是你得到 forall a. ... 的地方。 .

另一方面,当您声明多态类型签名时,您明确声明 foo是多态的(因此 [] 的类型不必与第一个参数的类型匹配)并且繁荣,你会得到模棱两可的类型变量。

现在,知道了这一点,让我们比较一下核心:
fib = 0:1:zipWith (+) fib (tail fib)
-----
fib :: forall a. Num a => [a]
[GblId, Arity=1]
fib =
\ (@ a) ($dNum :: Num a) ->
letrec {
fib1 [Occ=LoopBreaker] :: [a]
[LclId]
fib1 =
break<3>()
: @ a
(fromInteger @ a $dNum (__integer 0))
(break<2>()
: @ a
(fromInteger @ a $dNum (__integer 1))
(break<1>()
zipWith
@ a @ a @ a (+ @ a $dNum) fib1 (break<0>() tail @ a fib1))); } in
fib1

对于第二个:
fib :: Num a => [a]
fib = 0:1:zipWith (+) fib (tail fib)
-----
Rec {
fib [Occ=LoopBreaker] :: forall a. Num a => [a]
[GblId, Arity=1]
fib =
\ (@ a) ($dNum :: Num a) ->
break<3>()
: @ a
(fromInteger @ a $dNum (__integer 0))
(break<2>()
: @ a
(fromInteger @ a $dNum (__integer 1))
(break<1>()
zipWith
@ a
@ a
@ a
(+ @ a $dNum)
(fib @ a $dNum)
(break<0>() tail @ a (fib @ a $dNum))))
end Rec }

使用显式类型签名,如 foo以上,GHC要处理 fib作为潜在的多态递归值。我们可以通过一些不同的 Num字典到 fibzipWith (+) fib ...在这一点上,我们将不得不丢弃大部分列表,因为不同的 Num意味着不同 (+) .当然,一旦您使用优化进行编译,GHC 会注意到 Num字典在“递归调用”期间永远不会改变并优化它。

在上面的核心中,你可以看到 GHC 确实给出了 fib一个 Num字典(名为 $dNum )一次又一次。

因为 fib在整个绑定(bind)组的泛化完成之前,假设没有类型签名是单态的, fib子部分的类型与整个 fib 完全相同.多亏了这个, fib好像:
{-# LANGUAGE ScopedTypeVariables #-}
fib :: forall a. Num a => [a]
fib = fib'
where
fib' :: [a]
fib' = 0:1:zipWith (+) fib' (tail fib')

而且由于类型保持固定,您可以只使用开始时给出的一个字典。

关于haskell - NoMonomorphismRestriction 有助于保持共享?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11145904/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com