Рассмотрим следующий упрощенный пример:
import Control.Monad.State.Lazy
st :: [State Int Int]
st = [state (\s -> (s, s + 1)), undefined]
action1d = do
a <- sequence st
return $ map (2*) a
action2d = do
a <- sequence st
b <- sequence st
return $ zipWith (+) a b
main :: IO ()
main = do
print $ head $ evalState action1d 0
print $ head $ evalState action2d 0
Здесь, как в 1D, так и в 2D вычислениях, заголовок результата явно зависит только от заголовков входов (просто head a
для 1D действие и head a
и head b
для 2D-действия). Однако в 2D-расчете существует неявная зависимость b
(даже только его голова) от текущего состояния, и это состояние зависит от оценки полноты of a
, а не только его голова.
У вас есть похожая зависимость в вашем примере, хотя она скрыта использованием списков действий состояния.
Допустим, мы хотели запустить действие walk22_head = head $ walk 2 2
вручную и проверьте первое целое число в результирующем списке:
main = print $ head $ evalState walk22_head
Записав элементы списка состояний состояния st
явно:
st1, st2 :: State Int Int
st1 = state (\s -> (s, s+1))
st2 = undefined
мы можем написать walk22_head
as:
walk22_head = do
z <- st1
a <- walk21_head
b <- walk12_head
return $ zipWith (\x y -> x + y + z) a b
Обратите внимание, что это зависит только от определенного состояния действия st1
и головок walk 2 1
и walk 1 2
. Эти главы, в свою очередь, можно записать так:
walk21_head = do
z <- st1
a <- return [0] -- walk20_head
b <- walk11_head
return $ zipWith (\x y -> x + y + z) a b
walk12_head = do
z <- st1
a <- walk11_head
b <- return [0] -- walk02_head
return $ zipWith (\x y -> x + y + z) a b
Опять же, они зависят только от определенного состояния действия st1
и главы walk 1 1
.
Теперь давайте попробуем записать определение walk11_head
:
walk11_head = do
z <- st1
a <- return [0]
b <- return [0]
return $ zipWith (\x y -> x + y + z) a b
Это зависит только от действия определенного состояния st1
, поэтому при наличии этих определений, если мы запустим main
, мы получим определенный ответ :
> main
10
Но эти определения не точны! В каждом из walk 1 2
и walk 2 1
действие головы представляет собой последовательность действий, начиная с действия, которое вызывает walk11_head
, но продолжая действиями, основанными на walk11_tail
. Итак, более точные определения были бы:
walk21_head = do
z <- st1
a <- return [0] -- walk20_head
b <- walk11_head
_ <- walk11_tail -- side effect of the sequennce
return $ zipWith (\x y -> x + y + z) a b
walk12_head = do
z <- st1
a <- walk11_head
b <- return [0] -- walk02_head
_ <- walk11_tail -- side effect of the sequence
return $ zipWith (\x y -> x + y + z) a b
с:
walk11_tail = do
z <- undefined
a <- return [0]
b <- return [0]
return [zipWith (\x y -> x + y + z) a b]
С этими определениями нет проблем при выполнении walk12_head
и walk21_head
в изоляции:
> head $ evalState walk12_head 0
1
> head $ evalState walk21_head 0
1
Побочные эффекты состояния здесь не нужны для вычисления ответа и поэтому никогда не вызываются. Но невозможно запустить их оба в последовательности:
> head $ evalState (walk12_head >> walk21_head) 0
*** Exception: Prelude.undefined
CallStack (from HasCallStack):
error, called at libraries/base/GHC/Err.hs:78:14 in base:GHC.Err
undefined, called at Lazy2D_2.hs:41:8 in main:Main
Поэтому попытка запустить main
не удалась по той же причине:
> main
*** Exception: Prelude.undefined
CallStack (from HasCallStack):
error, called at libraries/base/GHC/Err.hs:78:14 in base:GHC.Err
undefined, called at Lazy2D_2.hs:41:8 in main:Main
, потому что при расчете walk22_head
, даже самое начало вычисления walk21_head
зависит от побочного эффекта состояния walk11_tail
, инициируемого walk12_head
.
Ваше первоначальное определение walk
ведет себя так же, как эти макеты:
> head $ evalState (head $ walk 1 2) 0
1
> head $ evalState (head $ walk 2 1) 0
1
> head $ evalState (head (walk 1 2) >> head (walk 2 1)) 0
*** Exception: Prelude.undefined
CallStack (from HasCallStack):
error, called at libraries/base/GHC/Err.hs:78:14 in base:GHC.Err
undefined, called at Lazy2D_0.hs:15:49 in main:Main
> head $ evalState (head (walk 2 2)) 0
*** Exception: Prelude.undefined
CallStack (from HasCallStack):
error, called at libraries/base/GHC/Err.hs:78:14 in base:GHC.Err
undefined, called at Lazy2D_0.hs:15:49 in main:Main
Сложно сказать, как это исправить. Ваш игрушечный пример был превосходен для иллюстрации проблемы, но не ясно, как состояние используется в вашей «реальной» проблеме, и если head $ walk 2 1
действительно имеет зависимость состояния от действий sequence
из walk 1 1
, вызванных head $ walk 1 2
.