Eddtothefullest
Eddtothefullest

Reputation: 25

MonadState gets stuck in an infinite recursive loop

[EDIT 3]

I have finally pinned down the problem: I added the put method for the MonadState instance:

instance MonadState CPUState VM where
    get = VM get
    put s = do
              liftIO $ putStrLn $ "Modifying state"
              liftIO $ putStrLn.show $ (ip s)
              state (\_ -> ((),s))

The infinite loop starts whenever the state needs to be updated:

let s' = (cpuState { ip = (ip cpuState) + 1 })
put s'

I am not really sure why... Could anyone please be kind enough to clarify why it keeps on recursively calling the put method?

[EDIT 2]

Now I know why it's hanging, and it's all due to this single line:

modify $ \s -> (cpuState { globalMemory = newGlobalMemory, ip = currentIP + 1 })

Before I made the newtype, this was working perfectly... How would I implement the same method on the MonadState instance?

[EDIT 1]

Thanks chi! I actually did something similar:

newtype VM a = VM{
    unwrapVM :: RWST [Quadruple] [String] (CPUState) IO a
} deriving (Functor, Applicative, Monad, MonadIO,
             MonadRWS [Quadruple] [String] CPUState)

instance MonadReader [Quadruple] VM where
    ask = VM ask

instance MonadWriter [String] VM where
    tell = VM . tell

instance MonadState CPUState VM where
    get = VM get

This compiles, but when I evaluate the RWST, it just hangs there indefinitely

I actually tried doing it your way, but I get the following compile errors (even with GeneralizedNewtypeDeriving):

• Expecting one more argument to ‘VM’
  Expected a type, but ‘VM’ has kind ‘* -> *’
• In the first argument of ‘MonadWriter’, namely ‘VM’

[Original Question]

Given a newtype that wraps an RWST:

newtype VM a = VM{
    unwrapVM :: RWST [Quadruple] [String] (CPUState) IO a
} deriving (Functor, Applicative, Monad, MonadIO)

Why isn't it possible to do something like this?

startVM :: [Quadruple] -> Memory -> Memory -> IO ()
startVM quads globalMemory localMemory = 
    do 
      (a,w) <- evalRWST (unwrapVM $ runVM) quads (setInitialCPUState globalMemory localMemory) 
      mapM_ (putStrLn) $ w 

runVM :: VM ()
runVM = do
          quadruples <-  ask     . . . .    [1] 
          cpuState <-  get       . . . .    [2]
          return ()

Haskell complains in runVM that there is no instance for MonadReader [1] and for MonadState [2], however it's clearly not that way, since VM is just a wrapper for a RWST, right?

Upvotes: 0

Views: 122

Answers (2)

Eddtothefullest
Eddtothefullest

Reputation: 25

The infinite loop was due to me calling state inside the put. I just changed it to:

instance MonadState CPUState VM where
    get = VM get
    put s = VM . put $ s 

For anyone that wants to hide the nested monad transformers, this is the way to go:

newtype VM a = VM{
    unwrapVM :: RWST [Quadruple] [String] (CPUState) IO a
} deriving (Functor, Applicative, Monad, MonadIO,MonadRWS [Quadruple] [String] CPUState)

instance MonadReader [Quadruple] VM where
    ask = VM ask

instance MonadWriter [String] VM where
    tell = VM . tell

instance MonadState CPUState VM where
    get = VM get
    put s = VM . put $ s 

Upvotes: 0

chi
chi

Reputation: 116139

You need to explicitly ask for the needed instances to be inherited.

newtype VM a = VM{
    unwrapVM :: RWST [Quadruple] [String] (CPUState) IO a
} deriving (Functor, Applicative, Monad, MonadIO, MonadState, MonadReader)
                                               -- ^^^^^^^^^^^^^^^^^^^^^^^

For this you probably need the extension GeneralisedNewtypeDeriving.

You need to be explicit on inheriting these instances, since otherwise you could define your own

instance MonadState VM where ...
instance MonadReader VM where ...

Indeed, it is very common to wrap an existing type T under a newtype W only to provide W with a different instance than the one for T.

Upvotes: 1

Related Questions