Ответ 1
- Соответствие шаблону
(_, _)
требует WHNF(f st, g st')
. Легко. - Соответствие шаблону
(_, (_,_))
требует WHNFg st'
. Здесь проблема, потому чтоg
является строгой, поэтому сначала ей нужноst'
в WHNF. Время выполнения находитst'
в шаблоне((_,st'), (_,_))
, поэтому, прежде чем он может перейти вst'
, ему нужно WHNF обоих кортежей. Посколькуg
является строгим, для этого сначала требуетсяst'
... и т.д.
Если вы сопоставляете результат g
с ленивым неопровержимым рисунком
(<+>) f g st = let ((_,st'), ~(_,st'')) = (f st, g st') in ((),st'')
тогда проблема исчезает, потому что это оценивается таким образом:
- Соответствие шаблону
(_, _)
требует WHNF(f st, g st')
. Легко. - Соответствие шаблону
(_, ~(_,_))
пока не требует ничего более. - Соответствие шаблону
((_,st'), ~(_,_))
требует WHNFf st
. К счастью, мы можем это выполнить, потому чтоst
не зависит от шаблона. - Теперь мы выполнили соответствие шаблону, время выполнения уже может быть продолжено с помощью
in ((),st'')
. Только на этом этапе создается неопровержимый шаблон~(_,st'')
, но теперь это уже не проблема, потому чтоst'
доступен здесь, так что это просто вопрос вычисленияg
один раз.
Исправления, которые вы испробовали, составляют g
нестрогий:
удалите деконструкцию
@(Foo _)
Без этого g
на самом деле не нужно искать свой аргумент для построения скелета результата, т.е. соответствие набора (_,st'')
может быть выполнено без предварительного запроса WHNF st'
.
измените
data Foo
наnewtype Foo
Это приводит к тому, что конструктор Foo
фактически не существует во время выполнения, поэтому нет ничего, что заставит шаблон [email protected](Foo _)
.
измените
noOp <+> someOp
наsomeOp <+> noOp
Как я уже говорил, цикл возникает только потому, что g
является строгим. Если вы поместите f
в свое положение, которое не является строгим, тогда проблем нет.