- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我在 ghci 中为偶数和奇数定义了两个相互递归列表,如下所示:
> let evens = 0:map (+1) odds; odds = map (+1) evens
然后我使用 :sp
咨询 thunk
> :sp evens
evens = _
> :sp odds
odds = _
> take 5 evens
[0,2,4,6,8]
> :sp evens
evens = 0 : 2 : 4 : 6 : 8 : _
:sp odds
odds = _
请注意,尽管 evens
已计算到第 5 个元素,但 odds
thunk 并未计算。我能想到一个直观的解释。 odds
必须显式调用才能进行评估:
> take 5 odds
[1,3,5,7,9]
>:sp odds
odds = 1 : 3 : 5 : 7 : 9 : _
但是,现在当我这样做时:
> take 10 evens
[0,2,4,6,8,10,12,14,16,18]
> :sp evens
evens = 0 : 2 : 4 : 6 : 8 : 10 : 12 : 14 : 16 : 18 : _
> :sp odds
odds = 1 : 3 : 5 : 7 : 9 : 11 : 13 : 15 : 17 : _
请注意,每当评估evens
时,现在如何评估odds
thunk?为什么odds
第一次不进行评估,而在第二次和所有后续评估中进行评估?发生了什么?
最佳答案
这与 GHC 编译相互递归绑定(bind)的方式有关(并且绑定(bind)是否具有显式类型签名存在差异)。
让我们编写以下简单的程序,该程序暴露了相同的问题,但消除了对整数重载或单态限制可能发挥的作用的所有怀疑:
module MutRec where
ft = False : map not tf
tf = map not ft
将其加载到 GHCi(我使用的是 7.6.3)中,结果是:
*MutRec> take 5 ft
[False,False,False,False,False]
*MutRec> :sp ft
ft = False : False : False : False : False : _
*MutRec> :sp tf
tf = _
让我们看看这个模块的核心代码
$ ghc -O0 MutRec -fforce-recomp -ddump-simpl -dsuppress-all
[1 of 1] Compiling MutRec ( MutRec.hs, MutRec.o )
==================== Tidy Core ====================
Result size of Tidy Core = {terms: 28, types: 42, coercions: 0}
Rec {
ft1_rkA
ft1_rkA = : False a_rkC
tf1_rkB
tf1_rkB = map not ft1_rkA
a_rkC
a_rkC = map not tf1_rkB
end Rec }
ds_rkD
ds_rkD = (ft1_rkA, tf1_rkB)
ft
ft = case ds_rkD of _ { (ft2_Xkp, tf2_Xkr) -> ft2_Xkp }
tf
tf = case ds_rkD of _ { (ft2_Xkq, tf2_Xks) -> tf2_Xks }
这说明了一切。相互递归的定义最终出现在 Rec
block 中,直接相互引用。但随后 GHC 正在构建一对 ds_rkD 并从该对中重新提取组件。这是一个额外的间接。它解释了为什么在 GHCi 中部分评估 ft
后,tf
的顶部仍然会显示为 thunk,即使下面已经进行了评估。事实上,我们可以验证仅对 tf 进行最少的评估就足以揭示这一点:
*MutRec> take 5 ft
[False,False,False,False,False]
*MutRec> :sp ft
ft = False : False : False : False : False : _
*MutRec> :sp tf
tf = _
Prelude MutRec> seq tf ()
()
Prelude MutRec> :sp tf
tf = True : True : True : True : _
如果我们向 ft
和 tf
添加显式类型签名或启用优化,则不会发生元组构造:
$ ghc -O MutRec -fforce-recomp -ddump-simpl -dsuppress-all
[1 of 1] Compiling MutRec ( MutRec.hs, MutRec.o )
==================== Tidy Core ====================
Result size of Tidy Core = {terms: 12, types: 11, coercions: 0}
Rec {
ft1
ft1 = map not tf
ft
ft = : False ft1
tf
tf = map not ft
end Rec }
现在 GHCi 的行为会更加自然。
<小时/>我查看了 GHC 来源,试图找出差异的原因行为。这似乎是多态绑定(bind)的类型推断工作方式的副作用。
如果绑定(bind)是多态的但没有类型签名,那么它的递归使用是单态的。这是 Hindley-Milner 中 GHC 也实现的限制。如果你想多态递归,您需要额外的类型签名。
为了在核心语言中忠实地对此进行建模,脱糖者制作了一个单态副本每个未注释的递归函数。这个单态版本用于递归调用,通用版本用于外部调用。即使是很小的东西你也能看到这一点诸如 rep
之类的函数(它是 repeat
的重新实现)。脱糖核心
rep x = x : rep x
是
rep
rep =
\ (@ a_aeM) ->
letrec {
rep_aeJ
rep_aeJ =
\ (x_aeH :: a_aeM) -> : @ a_aeM x_aeH (rep_aeJ x_aeH); } in
rep_aeJ
外部 rep
是多态的,因此开头的类型抽象 \(@ a_aeM) ->
。内部 rep_aeJ
是单态的,用于递归调用。
如果向 rep
添加显式类型注释
rep :: a -> [a]
rep x = x : rep x
那么递归调用就是多态版本,生成的Core就变成了更简单:
Rec {
rep
rep = \ (@ a_b) (x_aeH :: a_b) -> : @ a_b x_aeH (rep @ a_b x_aeH)
end Rec }
您可以看到类型参数 @ a_b
如何在开头被选取并重新应用在每个递归调用中rep
。
我们看到的相互递归绑定(bind)的元组构造只是一个这一原则的概括。您构建了相互的内部单态版本递归函数,然后将它们概括为元组,并提取多态元组的版本。
所有这一切的发生与绑定(bind)是否实际上是多态无关。它们递归就足够了。我认为GHC的这种行为完全是正确且正常,特别是因为优化可以解决性能影响。
关于haskell - 奇怪的 GHCi 惰性评估,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22491700/
我经常使用 ghci 进行少量计算,并使用 stack ghci 来处理我的实际项目。 为了使第一个更容易,我编写了一个 .ghci 文件,其中包含许多导入的模块,但其中一些模块不存在于我的堆栈项目中
我试图用这个来完善我的 GHCi:http://www.reddit.com/r/haskell/comments/144biy/pretty_output_in_ghci_howto_in_comm
我有一个 .ghci在我的本地项目目录中,另一个在我的 $HOME 中.当我做 stack ghci ,然后 $HOME/.ghci首先加载,然后是 $PWD/.ghci .是否可以只加载本地 .gh
在尝试将 ~/.ghci 文件更改为我的配置时,当我打开 GHCi 时出现此错误。 GHCi, version 7.6.3: http://www.haskell.org/ghc/ :? for h
所以我正在完成 Real World Haskell 的一些初始章节练习,我想知道 GHCi 中是否有一个选项可以让它在每个递归调用上显示带有参数的函数评估。例如,我写了一个简单版本的“map”,当我
我是 Haskell 的新手,在调试时我遇到了一个烦人的行为。 我使用 :break 添加断点 我运行 main 一切正常 我输入:继续完成执行 当我重新运行 main 时,断点不再命中,但断点没有被
从 ghc 7.6 更新到 7.10 后,您似乎无法 :m [Module]或 ghci> import [Module]其中 [Module.hs] 是您的手写模块文件,位于当前工作目录中。 似乎
我正在尝试编写一个 Erasthosthenes 函数的筛选器,该函数为用户提供从 2 到其上限的所有质数。所以我写了这段代码: main = do putStrLn "Upper Limit" g
这个问题在这里已经有了答案: How does GHCi pick names for type variables? (1 个回答) How are variable names chosen in
我现在应该真的知道这一点,但我不知道。我经常开发基于 Cabal 的软件包,并且刚刚成功运行了 cabal build .现在我想在 GHCi 中尝试一些东西。如果我运行 cabal repl ,然后
编写一个模块: module Foo where foo = 3.14 编译它: ghc -c Foo.hs 加载它: ghci -ignore-dot-ghci GHCi, version 7.8.
在检查不同整数类型的大小( minBound 、 maxBound )和“十进制表示的长度”时,我碰巧看到了一些奇怪的行为。 使用 GHCi: Prelude> :{ Prelude| let mi
考虑程序: l = [0..10] l' = map (+1) [0..10] 使用 GHCi 运行它,并键入 :sprint l 和 :sprint l' 将显示两个列表都未计算。但是,在运行 le
GHCi 中有没有办法基本上获得状态转储?我的意思是一个列表: 所有加载的运算符及其优先级、关联性和签名。 所有加载的类。 所有加载的数据、类型和新类型以及它们是哪些类的实例。 所有加载的函数都带有它
我刚开始学习 Haskell,很难理解 Haskell 程序的“流程”。 例如在 Python 中,我可以编写一个脚本,将其加载到解释器并查看结果: def cube(x): return x
A做了一个模块Timeit。我无法将其导入 GHCi。 模块: module Timeit (timeit, timeCatch) where import Data.Time.Clock timei
我刚刚安装了 Haskell Platform for Windows(版本 2011.2.0.1),并开始通过 HaskellQuestions.pdf 工作 第二个问题需要“x = 3”作为答案。
作为 Haskell 的新手,我正在努力解决以下差异(我确信这是有充分理由的)。也许我的问题只是源于对 GHCi 的误解,但当我能消除疑虑时,我晚上会睡得更好。 来了。如果,在将名称 foo 绑定(b
我正在试验 GHCi 的 :sprint命令。考虑以下: GHCi> xs = [1..10] :: [Int] GHCi> :sprint xs xs = _ GHCi> length xs 10
假设我有以下功能: minc = map (+1) natural = 1:minc natural 它似乎是这样展开的: 1:minc(1:minc(1:minc(1:minc(1:minc(1:m
我是一名优秀的程序员,十分优秀!