I have a simplified version of the standard interpreter monad transformer generated by FreeT
:
data InteractiveF p r a = Interact p (r -> a)
type Interactive p r = FreeT (InteractiveF p r)
p
is the "prompt", and r
is the "environment"...one would run this using something like:
runInteractive :: Monad m => (p -> m r) -> Interactive p r m a -> m a
runInteractive prompt iact = do
ran <- runFreeT iact
case ran of
Pure x -> return x
Free (Interact p f) -> do
response <- prompt p
runInteractive prompt (f resp)
instance MonadFix m => MonadFix (FreeT (InteractiveF p r)) m a)
mfix = -- ???
I feel like this type is more or less just a constrained version of StateT
...if anything, an Interactive p r IO
is I think a constrained version of IO
...I think...but... well, in any case, my intuiton says that there should be a good instance.
I tried writing one but I can't really seem to figure out. My closest attempt so far has been:
mfix f = FreeT (mfix (runFreeT . f . breakdown))
where
breakdown :: FreeF (InteractiveF p r) a (FreeT (InteractiveF p r) m a) -> a
breakdown (Pure x) = x
breakdown (Free (Interact p r)) = -- ...?
I also tried using a version taking advantage of the MonadFix
instance of the m
, but also no luck --
mfix f = FreeT $ do
rec ran <- runFreeT (f z)
z <- case ran of
Pure x -> return x
Free iact -> -- ...
return -- ...
Anyone know if this is really possible at all, or why it isn't? If it is, what's a good place for me to keep on looking?
Alternatively, in my actual application, I don't even really need to use FreeT
...I can just use Free
; that is, have Interactive
be just a monad and not just a monad transformer, and have
runInteractive :: Monad m => (p -> m r) -> Interactive p r a -> m a
runInteractive _ (Pure x) = return x
runInteractive prompt (Free (Interact p f) = do
response <- prompt p
runInteractive prompt (f response)
If something is possible for this case and not for the general FreeT case, I would also be happy :)
It is not possible to write a MonadFix m => MonadFix (Interactive p r)
instance.
Your InteractiveF
is the base functor of the well studied Moore machines. A Moore machine provides an output, in your case the prompt, then determines the next thing to do based on an input, in your case the environment. A Moore machine always outputs first.
data MooreF a b next = MooreF b (a -> next)
deriving (Functor)
If we follow C. A. McCann's argument about writing MonadFix
instances for Free
but constrain ourselves to the specific case of Free (MooreF a b)
, we will eventually determine that if there's a MonadFix
instance for Free (MooreF a b)
then there must exist a function
mooreFfix :: (next -> MooreF a b next) -> MooreF a b next
To write this function, we must construct a MooreF b (f :: a -> next)
. We don't have any b
s to output. It's conceivable that we could get a b
if we already had the next a
, but a Moore machine always outputs first.
You can write something close to mooreFfix
if you are reading just one a
ahead.
almostMooreFfix :: (next -> MooreF a b next) -> a -> MooreF a b next
almostMooreFfix f a = let (MooreF b g) = f (g a)
in (MooreF b g)
It then becomes imperative that f
be able to determine g
independently of the argument next
. All of the possible f
s to use are of the form f next = MooreF (f' next) g'
where f'
and g'
are some other functions.
almostMooreFFix :: (next -> b) -> (a -> next) -> a -> MooreF a b next
almostMooreFFix f' g' a = let (MooreF b g) = f (g a)
in (MooreF b g)
where
f next = MooreF (f' next) g'
With some equational reasoning we can replace f
on the right side of the let
almostMooreFFix :: (next -> b) -> (a -> next) -> a -> MooreF a b next
almostMooreFFix f' g' a = let (MooreF b g) = MooreF (f' (g a)) g'
in (MooreF b g)
We bind g
to g'
.
almostMooreFFix :: (next -> b) -> (a -> next) -> a -> MooreF a b next
almostMooreFFix f' g' a = let (MooreF b _) = MooreF (f' (g' a)) g'
in (MooreF b g')
When we bind b
to f' (g' a)
the let
becomes unnecessary and the function has no recursive knot.
almostMooreFFix :: (next -> b) -> (a -> next) -> a -> MooreF a b next
almostMooreFFix f' g' a = MooreF (f' (g' a)) g'
All of the almostMooreFFix
es that aren't undefined
don't even need a let
.
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