Implementing an MFunctor instance for RVarT

跟風遠走 提交于 2019-12-05 13:02:01

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 Lifts 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)
jpath

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!