Is it possible to implement an MFunctor
instance for RVarT
?
So far I've come up with the following:
{-# LANGUAGE RankNTypes #-}
import Data.RVar -- from rvar
import Control.Monad.Trans.Class (lift) -- from transformers
hoistRVarT :: Monad m => (forall t. n t -> m t) -> RVarT n a -> RVarT m a
hoistRVarT f rv = sampleRVarTWith (lift . f) rv
However this can not be used as a definition of hoist
for MFunctor
, due to the Monad
constraint on m
incurred by lift
. The problem is, that I couldn't find another way to lift the the resulting monad into RVarT
without lift
. But I think conceptually it should be possible, since RVarT
should be similar to StateT
and there is an MFunctor
instance for StateT
. The problem is that I couldn't find anything in the API of rvar
or random-fu
which exposed such functionality.
RVarT m a
is a newtype
for PromptT Prim m a
, where PromptT
is defined in Control.Monad.Prompt
. PromptT Prim m a
is a newtype
for Prompt (Lift Prim m) a
. This, in turn, is a newtype
for
forall b. (a -> b) -> (forall x. Lift Prim m x -> (x -> b) -> b) -> b
You can unwrap the whole thing with unsafeCoerce
:
fromRVarT :: RVarT m a -> (a -> b) -> (forall x. Lift Prim m x -> (x -> b) -> b) -> b
fromRVarT = unsafeCoerce
toRVarT :: (forall b. (a -> b) -> (forall x. Lift Prim m x -> (x -> b) -> b) -> b) -> RVarT m a
toRVarT = unsafeCoerce
Prim
isn't exported, but since you shouldn't need to touch it in the first place, and you're assembling and disassembling the whole thing with unsafeCoerce
, you can just define:
data Prim a
You can write an MFunctor
instance for Lift
:
instance MFunctor (Lift f) where
hoist _ (Effect p) = Effect p
hoist phi (Lift m) = Lift (phi m)
And then you can unwrap the RVarT
, hoist all the Lift
s passed to its prompting function, and wrap it again:
instance MFunctor RVarT where
hoist phi rv = toRVarT $ \done prm -> fromRVarT rv done (\l -> prm $ hoist phi l)
I found a trick that works for this and similar cases if you do not need to be able to actually use a value RVarT m
without a monad instance for m
. It works by deferring the application of the natural transformation until we actually need to get out a value. It would still be nice if there was a proper instance.
{-# LANGUAGE RankNTypes, ExistentialQuantification #-}
import Data.RVar
import Control.Monad.Trans.Class (lift)
import Control.Monad.Morph
import Control.Monad (ap)
hoistRVarT :: Monad m => (forall t. n t -> m t) -> RVarT n a -> RVarT m a
hoistRVarT f = sampleRVarTWith (lift . f)
data RVarTFun m a = forall n. RVarTFun
{ transformation :: forall t. n t -> m t
, rvart :: RVarT n a }
-- You can only get a value out if you have a monad for m.
getRVarTFun :: Monad m => RVarTFun m a -> RVarT m a
getRVarTFun (RVarTFun t ma) = hoistRVarT t ma
wrapRVarTFun :: RVarT m a -> RVarTFun m a
wrapRVarTFun = RVarTFun id
-- Actually the result is slightly stronger than MFunctor because we don't need
-- a Monad on n.
hoistRVarTFun :: (forall t. n t -> m t) -> RVarTFun n a -> RVarTFun m a
hoistRVarTFun f (RVarTFun t nx) = RVarTFun (f . t) nx
instance MFunctor RVarTFun where
hoist = hoistRVarTFun
A more general implementation of this can be found here.
来源:https://stackoverflow.com/questions/41797501/implementing-an-mfunctor-instance-for-rvart