Estoy tratando de seguir los consejos dados en Combine state with IO actions para construir un AppState junto con una món IO. Lo que obtuve es esto:¿Cómo ejecuto realmente una mónada StateT junto con 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])
Hay una parte de esto que no obtendré. Me molesta mucho tener script
y myMain
ymain
. También me molesta que tengo que ejecutar runState
dentro de myMain
y que tengo que alimentar un estado inicial en runStateT
en mi función principal. Estoy queriendo tener mi "script", por así decirlo, directamente en la función myMain porque todo el punto de myMain es poder ejecutar el apéndice y sumar directamente en myMain y justo al lado de las operaciones de impresión. Creo que debería ser capaz de hacer esto, en su lugar:
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
había pensado que el punto del transformador mónada fue así que puede ejecutar operaciones de mi mónada de estados en una función (como arriba) y levante las operaciones de IO en esa función. ¿Cuál es la forma correcta de configurar todo esto para que pueda eliminar una de las capas de indirección?
Además de solución de Daniel (que he marcado la solución), que también han encontrado algunas variaciones que también podría arrojar alguna luz sobre la situación. En primer lugar, la aplicación definitiva de mymain y principal:
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
Ahora, varias implementaciones de agregación y de sumST, además de Daniel:
append :: Integer -> AppState()
append v = state $ \(ST lst) -> ((), ST (lst ++ [v]))
sumST :: AppState Integer
sumST = state $ \(ST lst) -> (sum lst, ST lst)
y (Tenga en cuenta que sólo los cambios de declaración de tipo, de hecho se puede omitir la declaración de tipo completo!)
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)
se me ocurrió que la mónada AppState/Statet es no el mismo que el Mónada de estado básico, y estaba codificando tanto sumST como anexar para la mónada de estado. En cierto sentido, también tuvieron que ser elevados a la mónada StateT, aunque la forma correcta de pensar es que tenían que ser ejecutar en la mónada (por lo tanto, runState script new
).
No estoy seguro de obtenerlo completamente, pero trabajaré con él por un tiempo, leeré el código de MonadState y escribiré algo sobre esto cuando finalmente funcione en mi cabeza.
Ah, 'state' es más polimórfico de lo que había supuesto, porque tenía en mi cabeza por alguna razón que esta función era la excusa para no exportar un constructor' State'. ¡TIL! –