我有这个Haskell代码,当用GHC编译并运行时,在检测到循环时中止。
mutex
我认为不应该,这里有一些修改。它们中的每一个都会使循环消失:
data Foo = Foo ()
deriving (Eq,Show)
type Foop = Foo -> ((),Foo)
noOp :: Foop
noOp st = ((),st)
someOp :: Foop
someOp st@(Foo x) = ((),st)
(<+>) :: Foop -> Foop -> Foop
(<+>) f g st = let ((_,st'),(_,st'')) = ((f st),(g st')) in ((),st'')
main = print $ (noOp <+> someOp) $ Foo ()
更改为data Foo
newtype Foo
更改为(noOp <+> someOp)
(someOp <+> noOp)
这是GHC中的错误还是我对评估过程缺乏了解?
答案 0 :(得分:12)
(_, _)
要求(f st, g st')
的WHNF。容易。(_, (_,_))
要求g st'
的WHNF。这是问题所在,因为g
是严格的,因此它首先在WHNF中也需要st'
。运行时在模式st'
中找到((_,st'), (_,_))
,因此在它实际遍历到st'
之前,它需要WHNF两个元组。但由于g
是严格的,因此首先需要st'
...依此类推。如果您将g
的结果与懒惰无可辩驳的模式匹配
(<+>) f g st = let ((_,st'), ~(_,st'')) = (f st, g st') in ((),st'')
然后问题就消失了,因为这样就得到了评估:
(_, _)
要求(f st, g st')
的WHNF。容易。(_, ~(_,_))
暂时不再需要。((_,st'), ~(_,_))
要求f st
的WHNF。幸运的是,我们可以实现这一点,因为st
不依赖于模式。in ((),st'')
。只有在这一点上强制无可辩驳的模式~(_,st'')
,但现在这不再是问题了,因为st'
在这里可用,所以它只是计算g
一次的问题您尝试过的所有修正都等于g
非严格:
删除解构
@(Foo _)
如果不这样做,g
并不真正需要查看其构造结果框架的参数,即元组匹配(_,st'')
可以成功而无需首先要求{{1}的WHNF }。
将
st'
更改为data Foo
这导致newtype Foo
构造函数在运行时实际上并不存在,因此模式Foo
不会强制执行。
将
st@(Foo _)
更改为noOp <+> someOp
正如我所说,循环只是因为someOp <+> noOp
是严格的。如果你把g
置于不严格的位置,那就没问题。
答案 1 :(得分:8)
这不是一个错误 - 你刚刚发现了懒惰模式语义的一个棘手的角落。让我提出一个更简单的案例:
> data Id a = Id a
> let Id a = undefined ; Id b = Id 3 in b
3
> let (Id a, Id b) = (undefined, Id 3) in b
*** Exception: Prelude.undefined
区别在于第一个let
等同于
case undefined of
~(Id a) -> case Id 3 of
~(Id b) -> b
而第二个是
case (undefined, Id 3) of
~(Id a, Id b) -> b
第一个不会评估undefined
,除非要求a
(这不会发生)。
只要要么变量,第二个将模式匹配模式Id a
和Id b
,强制undefined
和Id 3
在这个过程中。
请注意,由于此问题,模式~(K1 (K2 x) (K3 y))
,~(K1 ~(K2 x) (K3 y))
,~(K1 (K2 x) ~(K3 y))
和~(K1 ~(K2 x) ~(K3 y))
具有不同的语义。
要修复您的代码,请尝试
let (~(_,st'),~(_,st'')) = ((f st),(g st')) in ((),st'')
或
let (_,st') = f st ; (_,st'') = g st' in ((),st'')