I'm using the FreeT type from the free library to write this function which "runs" an underlying StateT:
runStateFree
:: (Functor f, Monad m)
=> s
-> FreeT f (StateT s m) a
-> FreeT f m (a, s)
runStateFree s0 (FreeT x) = FreeT $ do
flip fmap (runStateT x s0) $ \(r, s1) -> case r of
Pure y -> Pure (y, s1)
Free z -> Free (runStateFree s1 <$> z)
However, I'm trying to convert it to work on FT, the church-encoded version, instead:
runStateF
:: (Functor f, Monad m)
=> s
-> FT f (StateT s m) a
-> FT f m (a, s)
runStateF s0 (FT x) = FT $ \ka kf -> ...
but I'm not quite having the same luck. Every sort of combination of things I get seems to not quite work out. The closest I've gotten is
runStateF s0 (FT x) = FT $ \ka kf ->
ka =<< runStateT (x pure (\n -> _ . kf (_ . n)) s0
But the type of the first hole is m r -> StateT s m r and the type the second hole is StateT s m r -> m r...which means we necessarily lose the state in the process.
I know that all FreeT functions are possible to write with FT. Is there a nice way to write this that doesn't involve round-tripping through FreeT (that is, in a way that requires explicitly matching on Pure and Free)? (I've tried manually inlining things but I don't know how to deal with the recursion using different ss in the definition of runStateFree). Or maybe this is one of those cases where the explicit recursive data type is necessarily more performant than the church (mu) encoding?
Here's the definition. There are no tricks in the implementation itself. Don't think and make it type check. Yes, at least one of these
fmapis morally questionable, but the difficulty is actually to convince ourselves it does the Right thing.We have two stateless functions (i.e., plain
m)and we must wrap them in two stateful (
StateT s m) variants with the signatures below. The comments that follow give some details about what is going on in the definition ofhandleS.There is an apparently dubious use of
fmapinhandleS, but it is valid as long asrunnever looks at the states produced byhandleS. It is almost immediately thrown away by one of theevalStateT.In theory, there exist terms of type
FT f (StateT s m) awhich break that invariant. In practice, that almost certainly doesn't occur; you would really have to go out of your way to do something morally wrong with those continuations.In the following complete gist, I also show how to test with QuickCheck that it is indeed equivalent to your initial version using
FreeT, with concrete evidence that the above invariant holds:https://gist.github.com/Lysxia/a0afa3ca2ea9e39b400cde25b5012d18