У меня есть несколько действий монады State
. Некоторые из действий принимают решения, основанные на текущем состоянии и других входах, которые могут генерировать результат. Два типа действий вызывают друг друга.Составляющие действия трансформатора состояния и состояния
Я смоделировал эти два типа действия с помощью State
и StateT Maybe
. Следующий (надуманный) пример показывает мой текущий подход.
{-# LANGUAGE MultiWayIf #-}
import Control.Monad (guard)
import Control.Monad.Identity (runIdentity)
import Control.Monad.Trans.State
type Producer = Int -> State [Int] Int
type MaybeProducer = Int -> StateT [Int] Maybe Int
produce :: Producer
produce n
| n <= 0 = return 0
| otherwise = do accum <- get
let mRes = runStateT (maybeProduce n) accum
if | Just res <- mRes -> StateT $ const (return res)
| otherwise -> do res <- produce (n - 1)
return $ res + n
maybeProduce :: MaybeProducer
maybeProduce n = do guard $ odd n
modify (n:)
mapStateT (return . runIdentity) $
do res <- produce (n - 1)
return $ res + n
Я факторизовал отделение проверки от действий (таким образом, превращая их в простые действия государства), потому что сама проверка очень запутанная (80% работ) и обеспечиваю необходимые привязки в действии. Я не хочу рекламировать действия State
до StateT Maybe
, потому что он представляет собой неточную модель.
Есть ли лучший или более простой способ, который мне не хватает? В частности, мне не нравится дуэт mapStateT
/runStateT
, но это кажется необходимым.
PS: Я знаю пример, на самом деле является Writer
, но я использовал State
, чтобы лучше отражать реальный случай
'mapStateT' отлично, ИМО. Я просто использую 'Just. runIdentity' в качестве аргумента. – arrowd
Примерно такой же вопрос (хотя и более теоретический): http://stackoverflow.com/questions/4138671/combining-statet-and-state-monads – gcnew