4

having some trouble with a group of monads I'm trying to combine.

I'm using monad-coroutine, State and lens (as I have deeply nested state).

I had an initial approach where there was a working solution. The main point here is that I can request to execute IO tasks outside of Coroutine.

{-# 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

Now, at some point a simple State became not enough and I am in a need of ST. I started to try to get the ST inside StateT but for some reason cannot come up with an idea how to properly handle IO outside of coroutine. Is there any way to come up with similar runMyType when there is a change in the Coroutine?

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

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

Whatever I try to come up with throws some error about s escaping or Couldn't match type ‘s’ with ‘s2’ and so on... Maybe some other order of monad stacking will help? Or is it at all possible?

And another question if you have some time: what is the difference between the above MyType s and this one:

type MyType = forall s. Coroutine IORequest (StateT (MyState s) (ST s))
ksaveljev
  • 550
  • 2
  • 16
  • In introducing `MyType`, you seem to be using a new `MyState` (one with kind `* -> *` as opposed to just `*`). Is that `data MyState s = MyState s`? Also, `initialState` is fine, it just has the wrong signature (`Num a => ST s (MyState (STRef s a))`). You may want to clarify what exactly you are asking there. That said, the last question is very interesting - you will want to read up on rankNTypes. – Alec Nov 29 '16 at 09:54
  • @alec, you are correct, it is `data MyState s = MyState s`. What I am asking for (sorry if it is unclear) is how (if possible) to implement `runMyType` with new `MyType s` because I need to run some actions in ST monad there but at the same time the requests from `Coroutine` are `IO` – ksaveljev Nov 29 '16 at 10:30
  • I don't think you can do that, short of using [unsafe operations](https://hackage.haskell.org/package/base-4.9.0.0/docs/Control-Monad-ST-Unsafe.html). Your `runMyType` depends crucially on the ability to suspend the state of the computation and then feed it back in to the next round of `runState`, but with `ST`, once you `runST` up to a `RunIO` request, you won't be able to go back to "the same `s`". – Cactus Nov 30 '16 at 02:13
  • You're getting this error because of the way how `ST` works. Try to read this explanation: https://en.wikibooks.org/wiki/Haskell/Existentially_quantified_types#Example:_runST You basically can't return `MyState s` from `ST s` inside `runST`. If you clarify why you need `ST` and give some example, then, probably, another solution can be found or your current may be patched. – Shersh Nov 30 '16 at 12:39
  • `data MyState s = MyState s` simply doesn't make sense - the state token `s` which is passed to `ST` is not a 'real' type, it should only ever be instantiated with type variables, so using it in a relevant position (like as the type of a value) is simply a semantic error. Maybe you want `data MyState s = MyState (STRef s Int)` or `initialState :: ST s (STRef s MyState)` (in the latter case, with the original `data MyState = MyState Int` type). – user2407038 Nov 30 '16 at 21:54
  • Yes, my mistake about `MyState`. It is supposed to be something like `data MyState s = MyState { tmp :: STRef s Int, ... }` (basically huge deeply nested state with some STRefs, STUArrays and ST vectors. @Shersh I was experimenting with coroutine to have pure logic which requests to perfom IO outside of coroutine. At some point it became clear that for some parts STRefs would be desired. That is why I tried to see if something with ST and Coroutine would work – ksaveljev Dec 01 '16 at 13:15

0 Answers0