Сопрограмма с StateT и ST и IO

Возникли проблемы с группой монад, которые я пытаюсь объединить.

Я использую монад-сопрограмму, State и линзу (так как у меня глубоко вложенное состояние).

У меня был первоначальный подход, где было рабочее решение. Главное здесь то, что я могу запросить выполнение IO задачи за пределами сопрограммы.

{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleInstances         #-}
{-# LANGUAGE MultiParamTypeClasses     #-}
{-# LANGUAGE UndecidableInstances      #-}

module Main where

import           Control.Monad.Coroutine      (Coroutine(..), suspend, resume)
import           Control.Monad.State          (State, MonadState, MonadIO)
import           Control.Monad.State          (lift, get, put, liftIO, runState)
import           System.Environment           (getArgs)

type MyType = Coroutine IORequest (State MyState)

instance (MonadState s m) => MonadState s (Coroutine IORequest m) where
    get = lift get
    put = lift . put

io :: MonadIO m => IO a -> m a
io = liftIO

data IORequest x = forall a. RunIO (IO a) (a -> x)

instance Functor IORequest where
    fmap f (RunIO x g) = RunIO x (f . g)

data MyState = MyState { _someInt :: Int }

initialState :: MyState
initialState = MyState 1

request :: Monad m => IO a -> Coroutine IORequest m a
request x = suspend (RunIO x return)

myLogic :: MyType [String]
myLogic = do
    args <- request (io getArgs)
    request (io (print args))
    -- do a lot of useful stuff here
    return args

runMyType :: MyType [String] -> MyState -> IO ()
runMyType logic state = do
    let (req, state') = runState (resume logic) state
    case req of
        Left (RunIO cmd q') -> do
            result <- cmd
            runMyType (q' result) state'
        Right _ -> return ()

main :: IO ()
main = runMyType myLogic initialState

Теперь, в какой-то момент простого государства стало недостаточно, и я нуждаюсь в ST. Я начал пытаться получить ST внутри StateT но по какой-то причине не может придумать, как правильно обращаться IO вне сопрограммы. Есть ли способ придумать подобное runMyType когда есть изменение в Coroutine?

type MyType s = Coroutine IORequest (StateT (MyState s) (ST s))

initialState :: ST s (MyState s)
initialState = do
    a <- newSTRef 0
    return (MyState a)

Все, что я пытаюсь придумать, приводит к ошибке s убегая или Couldn't match type ‘s’ with ‘s2’ и так далее... Может быть, поможет какой-то другой порядок укладки монад? Или это вообще возможно?

И еще один вопрос, если у вас есть время: в чем разница между MyType s и этот:

type MyType = forall s. Coroutine IORequest (StateT (MyState s) (ST s))

0 ответов

Другие вопросы по тегам