Я пытаюсь следовать совету, данному в разделе Объединить состояние с действиями ввода-вывода для создания AppState вместе с монадой IO. Что я получил, так это:
module Main where
import Control.Monad.State
import Control.Monad.Trans
data ST = ST [Integer] deriving (Show)
type AppState = StateT ST IO
new = ST []
append :: Integer -> State ST ()
append v = state $ \(ST lst) -> ((), ST (lst ++ [v]))
sumST :: State ST Integer
sumST = state $ \(ST lst) -> (sum lst, ST lst)
script = do
append 5
append 10
append 15
sumST
myMain :: AppState ()
myMain = do
liftIO $ putStrLn "myMain start"
let (res, st) = runState script new
liftIO $ putStrLn $ show res
liftIO $ putStrLn "myMain stop"
main = runStateT myMain (ST [15])
Есть часть этого, которую я не понимаю. Меня очень беспокоит, что у меня есть script
и myMain
и main
. Меня также беспокоит, что я должен выполнить runState
внутри myMain
и что я должен передать начальное состояние в runStateT
в моей основной функции. Я хочу, чтобы мой «скрипт», так сказать, находился непосредственно в функции myMain, потому что весь смысл myMain заключается в том, чтобы иметь возможность запускать добавление и суммирование непосредственно в myMain и прямо рядом с операциями печати. Я думаю, что я должен быть в состоянии сделать это вместо этого:
myMain :: AppState ()
myMain = do
liftIO $ putStrLn "myMain start"
append 5
append 10
append 15
r <- sumST
liftIO $ putStrLn $ show res
liftIO $ putStrLn "myMain stop"
main = runState myMain
Я думал, что цель монадного преобразователя заключается в том, чтобы я мог выполнять свои операции с монадой State в функции (как указано выше) и поднимать операции ввода-вывода в эту функцию. Как правильно настроить все это, чтобы я мог удалить один из уровней косвенности?
В дополнение к решению Даниэля (которое я пометил как решение), я также нашел несколько вариантов, которые также могут пролить свет на ситуацию. Во-первых, окончательная реализация myMain и main:
myMain :: AppState ()
myMain = do
liftIO $ putStrLn "myMain start"
append 5
append 10
append 15
res <- sumST
liftIO $ putStrLn $ show res
liftIO $ putStrLn "myMain stop"
main = runStateT myMain new
Теперь различные реализации append и sumST, помимо Дэниела:
append :: Integer -> AppState ()
append v = state $ \(ST lst) -> ((), ST (lst ++ [v]))
sumST :: AppState Integer
sumST = state $ \(ST lst) -> (sum lst, ST lst)
и (обратите внимание, что изменяется только объявление типа; на самом деле вы можете полностью опустить объявление типа!)
append :: MonadState ST m => Integer -> m ()
append v = state $ \(ST lst) -> ((), ST (lst ++ [v]))
sumST :: MonadState ST m => m Integer
sumST = state $ \(ST lst) -> (sum lst, ST lst)
Мне пришло в голову, что монада AppState/StateT не совпадает с базовой монадой State, и я кодировал как sumST, так и append для монады State. В некотором смысле их также нужно было поднимать в монаду StateT, хотя правильный способ думать о in состоит в том, что они должны были запускаться в монаде (отсюда runState script new
).
Я не уверен, что полностью понял это, но я поработаю с ним некоторое время, прочитаю код MonadState и напишу что-нибудь об этом, когда это наконец заработает в моей голове.
state
более полиморфен, чем я предполагал, потому что по какой-то причине я держал в голове, что эта функция была извинением за то, что конструкторState
больше не экспортируется. ТИЛЬ! - person Daniel Wagner   schedule 06.07.2012