Haskell程序以" loop"中止但我认为不应该

时间:2016-06-15 21:33:32

标签: haskell

我有这个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中的错误还是我对评估过程缺乏了解?

2 个答案:

答案 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 aId b,强制undefinedId 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'')