The following program uses a backwards-traveling state as provided by the Tardis monad.
{-# LANGUAGE RecursiveDo #-}
import Control.Monad.Tardis
lastOccurrence :: Int -> Tardis [Int] () Bool
lastOccurrence x = mdo
sendPast (x : xs)
xs <- getFuture
return (not (elem x xs))
lastOccurrences :: [Int] -> Tardis [Int] () [Bool]
lastOccurrences xs = mapM lastOccurrence xs
main :: IO ()
main =
print $ flip evalTardis ([], ()) $ lastOccurrences [3,4,6,7,4,3,5,7]
How can I replace the Tardis monad with the reverse State monad?
With my following proposal, main
loops forever instead of printing
[False,False,True,False,True,True,True,True]
as with the above program.
{-# LANGUAGE RecursiveDo #-}
import Control.Monad.RevState
lastOccurrence :: Int -> State [Int] Bool
lastOccurrence x = mdo
put (x : xs)
xs <- get
return (not (elem x xs))
lastOccurrences :: [Int] -> State [Int] [Bool]
lastOccurrences xs = mapM lastOccurrence xs
main :: IO ()
main =
print $ flip evalState [] $ lastOccurrences [3,4,6,7,4,3,5,7]
I have now downloaded the source of both Tardis
and RevState
, and I started hacking on them until they are almost the same:
Trans.{Tarids,RevState}
modules, so that I don't have to bother with the typeclassesTardis
Tardis
to State
After a bit of reordering of code, I ended up in a situation where your Tardis
-using example still works and your RevState
-using example still doesn't work, and their difference is minimal.
What is that minimal difference, you ask? Unsurprisingly, the MonadFix
instance. Tardis
has this:
instance MonadFix m => MonadFix (TardisT bw fw m) where
mfix f = TardisT $ \s -> do
rec (x, s') <- runTardisT (f x) s
return (x, s')
whereas RevState
has this:
instance MonadFix m => MonadFix (StateT s m) where
mfix f = StateT $ \s ->
mfix (\(x, _) -> runStateT (f x) s)
While they seem similar, the big difference is that the RevState
one is strict in the tuple constructor, whereas the Tardis
one is lazy. (see e.g. the GHC documentation on RecursiveDo
to see that the Tardis
one desugars into an irrefutable pattern match in the lambda passed to mfix
).
Indeed, changing the implementation of RevState
so that
instance MonadFix m => MonadFix (StateT s m) where
mfix f = StateT $ \s -> do
mfix (\ ~(x, _) -> runStateT (f x) s)
fixes your original RevState
-using program.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With