Есть ли функция replicateM для внутренней монады в преобразователе монад?

Предположим, у меня есть что-то вроде этого:

data Environment = ...
data MyState = ...
data Report  = ...

updateState :: Environment -> MyState -> MyState
updateState = ...

report :: MyState -> Report
report = ...

foo :: ReaderT Environment (State MyState) Report 
foo = do env   <- ask
         state <- lift get
         let newState = updateState env state
         lift $ put newState
         return $ report newState

Что у меня в голове, так это моделирование временного процесса, в котором у меня есть параметры, которые будут храниться в Environment, динамическое состояние будет храниться в MyState, а информация, которую я хочу собрать на каждом временном шаге моделирования, будет храниться в Report.

Теперь я не хочу выполнять много шагов этой симуляции и получать список с отчетами для каждого временного шага.

Обычно я делал это без ReaderT и передал параметры следующим образом:

 foo :: Enviroment -> State MyState Report

Тогда я бы просто сделал:

 manySteps :: Int -> Enviroment -> State MyState [Report]
 manySteps n env = replicate n $ (foo env) 

Я путаюсь с типами lift и replicateM. Есть ли комбинация, которая воспроизвела бы монаду State MyState внутри трансформатора?

В будущем я заменю ReaderT Environment (State MyState) Report на ReaderT Environment (StateT MyState (Rand StdGen)) Report, так что лучше все сделать правильно, прежде чем иметь этот тип монстра :(.

редактировать: как побочный вопрос - есть ли лучшая стратегия, чем использование ReaderT Environment (State MyState) Report ?


person Rafael S. Calsaverini    schedule 16.03.2011    source источник
comment
Стратегия использования ReaderT и StateT выглядит разумной. Для реального использования я бы посоветовал не использовать ask,get,put напрямую, определить короткий псевдоним или помощник с описательным именем и использовать его вместо этого. Это позволяет вам редактировать реализацию стека Monad и обновлять только хелперы.   -  person Chris Kuklewicz    schedule 16.03.2011


Ответы (2)


Конкретный пример replicateM здесь:

import Control.Monad
import Control.Monad.Reader
import Control.Monad.State

data Environment = E Int deriving Show
data MyState = S Int deriving Show
data Report  = R String deriving Show

updateState :: Environment -> MyState -> MyState
updateState (E step) (S val) = S $! val + step

report :: MyState -> Report
report (S val) = R (show val)

foo :: ReaderT Environment (State MyState) Report 
foo = do env   <- ask
         state <- get -- lift was not needed
         let newState = updateState env state
         put newState -- lift was not needed
         return $ report newState

run e s m = runState (runReaderT m e) s

Обратите внимание, что я удалил «лифт», так как ReaderT имеет сквозной экземпляр MonadState. Запуск foo однажды дает:

*Main> run (E 10) (S 5) foo
(R "15",S 15)

Я могу запустить foo семь раз подряд:

*Main> run (E 10) (S 5) (replicateM 7 foo)
([R "15",R "25",R "35",R "45",R "55",R "65",R "75"],S 75)

Что из вышеизложенного требует дополнительных пояснений?

person Chris Kuklewicz    schedule 16.03.2011
comment
гм... теперь я вижу источник путаницы. Я думал, что если я сделаю replicateM n foo, то получу ReaderT Environment [(State MyState) Report]. Теперь я вижу, что это ОЧЕВИДНО не так. Тип даже не имеет смысла... - person Rafael S. Calsaverini; 16.03.2011
comment
Как вы избавились от неоднозначности для модуля Control.Monad.State в GHCi? (у меня есть пакеты mtl и monads-tf) - person ony; 16.03.2011
comment
@ony, вы можете использовать «ghci -XPackageImports ...» и «import mtl Control.Monad.State» или «ghci -package mtl ...» - person Chris Kuklewicz; 16.03.2011

Иногда достаточно подтвердить тип, если вы не уверены, как что-то использовать.

-- import Control.Monad.State
import Control.Monad
import Control.Monad.Trans.Reader

data Environment
data MyState
data Report
data State a b
instance Monad (State a)

foo = undefined :: ReaderT Environment (State MyState) Report

Чем в GHCi

*Main> :t flip replicateM foo
flip replicateM foo
  :: Int -> ReaderT Environment (State MyState) [Report]
person ony    schedule 16.03.2011