Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bearriver: RandT and other transformers don't work directly #65

Closed
turion opened this issue Dec 4, 2017 · 7 comments
Closed

bearriver: RandT and other transformers don't work directly #65

turion opened this issue Dec 4, 2017 · 7 comments

Comments

@turion
Copy link
Contributor

turion commented Dec 4, 2017

Disclaimer: I haven't tested this yet.

It seems to me that RandT is not usable yet the way we'd like it to use.

Typical dunai use case

myRandThing :: MSF (RandT g m) () a
myRandThing = _

myOtherRandThing :: MSF (RandT g m) a ()
myOtherRandThing = _

main = do
  g <- newStdGen
  reactimate $ evalRandS (myRandThing >>> myOtherRandThing) g

Problem in bearriver

This doesn't work in bearriver since an SF m a b is an MSF (ReaderT DTime m) a b, which means that the outermost monad is always a ReaderT. To remove the RandT layer, it must first be commuted past the ReaderT layer, i.e. one must define:

commute :: ReaderT r (RandT g m) a -> RandT g (ReaderT r m) a
commute = _

Then one needs to hoist an SF (RandT g m) a b along this monad morphism and then apply evalRandS, i.e.:

evalRandSF :: SF (RandT g m) a b -> g -> SF m a b
evalRandSF sf = evalRandS $ liftMSFPurer commute sf

Not sure whether the names are ok like this or should be improved.

Other monads

This is actually an issue for other transformers as well. We should maybe think about a more general solution, i.e. we've already axiomatised elsewhere what it means to be able to commute past a ReaderT layer.

@thalerjonathan
Copy link
Contributor

I had the same problem but instead of the RandT I am using StateT (they are isomorph as RandT is implemented using a StateT). I was able to solve it with the approach mentioned by @turion above. Here is my code, I hope it provides some help:

    type Agent m o d e = SF (StateT (AgentOut m o d e) m) (AgentIn o d e, e) e
    data AgentIn o d e = ...
    data AgentOut m o d e =  ... 

    runAgent :: Monad m 
             => SF m 
                  (Agent m o d e, AgentIn o d e, e)
                  (AgentOut m o d e, (e, Agent m o d e))
    runAgent = runStateSF_ runAgentAux agentOut
      where
        runAgentAux :: Monad m
                    => SF (StateT (AgentOut m o d e) m) 
                        (Agent m o d e, AgentIn o d e, e) 
                        (e, Agent m o d e)
        runAgentAux = arrM (\(sf, ain, e) -> unMSF sf (ain, e))

    runStateSF_ :: Monad m => SF (StateT s m) a b -> s -> SF m a (s, b)
    runStateSF_ sf = runStateS_ $ liftMSFPurer commute sf

    commute :: Monad m => ReaderT r (StateT s m) a -> StateT s (ReaderT r m) a
    commute (ReaderT read) = 
      StateT (\s -> 
        (ReaderT (\r -> let (StateT st) = read r
                        in  st s)))

@turion
Copy link
Contributor Author

turion commented Dec 11, 2017

Wow. I just figured out that ReaderT commutes past all monad transformers that are also functors in the category of monads (so essentially all usual ones except ContT):

import Control.Monad.Morph
import Control.Monad.Trans.Class
import Control.Monad.Trans.Reader

commuteReaderTF :: (MonadTrans t, MFunctor t, Monad m) => t (ReaderT r m) a -> ReaderT r (t m) a
commuteReaderTF trma = ReaderT $ \r -> hoist (flip runReaderT r) trma

@turion
Copy link
Contributor Author

turion commented Dec 11, 2017

Note that it's in general impossible to commute ReaderT the other way. Imagine e.g.:

dontpushme :: Monad m => ReaderT Bool (ExceptT () m) ()
dontpushme = ReaderT $ \b -> if b then throwE () else return ()

Depending on the environment variable b, the inner transformer will throw an error. It's impossible to push ReaderT down past ExceptT, since then the decision whether to throw an exception or not cannot depend on b anymore.

So we'd still be left with implementing those commutations for every single transformer that we want to handle in bearriver.

@turion
Copy link
Contributor Author

turion commented Dec 11, 2017

Sorry, my previous comment is incorrect. I should have said that ReaderT r (ExceptT e m) a cannot be mapped to Either e (ReaderT r m a) == Either e (r -> m a), but that's fortunately not the same as ExceptT e (ReaderT r m) a == r -> m (Either e a)! So ExceptT is a bad example, and I don't know a good one. Still it seems like there is no good way how to push the ReaderT layer down the stack.

@thalerjonathan
Copy link
Contributor

I am currently experimenting with STM: what I want to do is to run a SF which has a (RandT g STM) as Monad Stack concurrently using an async and committing the STM action using atomically. I have no idea how to approach this problem at the moment - is it even possible?

type Agent m o d = SF (RandT g STM) (AgentIn d) (AgentOut o d)

runAgents :: RandomGen g
          => SF (RandT g IO)
              ([Agent (RandT g STM) o d], [AgentIn d]) 
              ([Agent (RandT g STM) o d], [AgentOut o d])
runAgents = readerS $ proc (dt, (sfs, ins)) -> do
    let asIns = zipWith (\sf ain -> (dt, (ain, sf))) sfs ins
    arets <- mapMSF (runReaderS runAgentParallelSF) -< asIns
    let (aos, sfs') = unzip arets
    returnA -< (sfs', aos)

runAgentParallelSF :: RandomGen g
                   => SF (RandT g IO)
                      (AgentIn d, Agent (RandT g STM) o d)
                      (AgentOut o d, Agent (RandT g STM) o d)
runAgentParallelSF = ?

runAgentParallel :: AgentIn d 
                 -> Agent (RandT g STM) o d
                 -> IO (AgentOut o d, Agent (RandT g STM) o d)
runAgentParallel aid sf = do
  -- how to extract the STM from sf?
  -- let stmAction = ...
  a <- async $ atomically stmAction
  (ao, sf') <- wait a
  return (ao, sf')

@turion
Copy link
Contributor Author

turion commented Dec 12, 2017 via email

@turion turion changed the title RandT doesn't work completely in bearriver RandT and other transformers don't work directly in bearriver Jan 9, 2018
@turion turion removed the beginner label Jan 9, 2018
@ivanperez-keera ivanperez-keera changed the title RandT and other transformers don't work directly in bearriver bearriver: RandT and other transformers don't work directly Mar 3, 2018
@ivanperez-keera
Copy link
Owner

This has been stuck for a while.

Closing due to lack of activity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants