Oll*_*ieB 6 haskell monad-transformers reader-monad
我正在从许多不同的地方汇集代码,我正在尝试处理以下问题:
我有一个变压器堆栈,具有以下简化类型:
action :: m (ReaderT r IO) a
Run Code Online (Sandbox Code Playgroud)
我正在尝试在不同堆栈的上下文中使用该操作,该堆栈具有不同的读取器环境:
desired :: m (ReaderT r' IO) a
Run Code Online (Sandbox Code Playgroud)
我当然可以提供
f :: r' -> r
Run Code Online (Sandbox Code Playgroud)
things :: m (ReaderT r' IO) ()
things = do
-- ... some stuff
-- <want to use action here>
action :: m (ReaderT r IO) a -- broken
-- ... more stuff
pure ()
Run Code Online (Sandbox Code Playgroud)
withReaderT :: (r' -> r) -> ReaderT r m a -> ReaderT r' m a
Run Code Online (Sandbox Code Playgroud)
这有一个问题,ReaderT是外部monad,而我想在内部使用它.
我也认为这可能与MonadBase或MonadTransControl有关,但我不熟悉它们的工作原理.
我不认为写一个带签名的函数是可能的:
changeReaderT :: (MonadTrans m)
=> (r -> r')
-> m (ReaderT r IO) a
-> m (ReaderT r' IO) a
Run Code Online (Sandbox Code Playgroud)
问题在于,一般来说,唯一可能的操作就是第二个论点是将它提升到t (m (ReaderT r IO)) a一些monad变压器t,它不会给你任何东西.
也就是说,MonadTrans m仅限制约束并不能提供足够的结构来完成您想要的任务.你要么必须m要像一个类型类的实例MFunctor中mmorph,允许您通过提供功能类似修改一般的方式单子堆栈的内层包装:
hoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b
Run Code Online (Sandbox Code Playgroud)
(这就是@Juan Pablo Santos所说的),否则你需要有能力深入研究mmonad变换器的结构以部分运行和重建它(这将是变压器特定的).
如果您的包已经由包支持的变换器组成,那么第一种方法(hoist从mmorph包中使用)将是最方便m的mmorph.例如,以下类型检查,您不必编写任何实例:
type M n = MaybeT (StateT String n)
action :: M (ReaderT Double IO) a
action = undefined
f :: Int -> Double
f = fromIntegral
desired :: M (ReaderT Int IO) a
desired = (hoist $ hoist $ withReaderT fromIntegral) action
Run Code Online (Sandbox Code Playgroud)
你需要hoist为每一层提供一个M.
第二种方法避免了hoist必需的MFunctor实例,但需要根据您的具体情况进行定制M.对于上面的类型,它看起来像:
desired' :: M (ReaderT Int IO) a
desired' = MaybeT $ StateT $ \s ->
(withReaderT fromIntegral . flip runStateT s . runMaybeT) action
Run Code Online (Sandbox Code Playgroud)
你基本上需要将monad运行到ReaderT图层然后重新构建它StateT,小心处理图层.这正是MFunctor实例mmorph自动执行的操作.