[英]Modifying inner reader in a transformer stack
我正在從許多不同的地方匯集代碼,我正在嘗試處理以下問題:
我有一個變壓器堆棧,具有以下簡化類型:
action :: m (ReaderT r IO) a
我正在嘗試在不同堆棧的上下文中使用該操作,該堆棧具有不同的讀取器環境:
desired :: m (ReaderT r' IO) a
我當然可以提供
f :: r' -> r
things :: m (ReaderT r' IO) ()
things = do
-- ... some stuff
-- <want to use action here>
action :: m (ReaderT r IO) a -- broken
-- ... more stuff
pure ()
withReaderT :: (r' -> r) -> ReaderT r m a -> ReaderT r' m a
這有一個問題,ReaderT是外部monad,而我想在內部使用它。
我也認為這可能與MonadBase或MonadTransControl有關,但我不熟悉它們的工作原理。
我不認為寫一個帶簽名的函數是可能的:
changeReaderT :: (MonadTrans m)
=> (r -> r')
-> m (ReaderT r IO) a
-> m (ReaderT r' IO) a
問題在於,一般來說,第二個參數的唯一可能操作是將其提升到t (m (ReaderT r IO)) a
對於某些monad變換器t
,它不會給你任何東西。
也就是說, MonadTrans m
約束本身並沒有提供足夠的結構來做你想要的。 您需要m
作為mmorph
包中類似MFunctor
的類型類的實例,它允許您通過提供如下函數來以一般方式修改monad堆棧的內層:
hoist :: Monad m => (forall a. m a -> n a) -> t m b -> t n b
(這就是@Juan Pablo Santos所說的),否則你需要有能力挖掘m
monad變壓器的結構以部分運行和重建它(這將是變壓器特定的)。
如果你的m
已經由mmorph
包支持的變換器組成,那么第一種方法(使用mmorph
包中的hoist
)將是最方便的。 例如,以下類型檢查,您不必編寫任何實例:
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
你需要為M
每一層hoist
一個。
第二種方法避免了hoist
和必需的MFunctor
實例,但需要針對您的特定M
定制。 對於上面的類型,它看起來像:
desired' :: M (ReaderT Int IO) a
desired' = MaybeT $ StateT $ \s ->
(withReaderT fromIntegral . flip runStateT s . runMaybeT) action
你基本上需要將monad運行到ReaderT
層然后重新StateT
,小心處理像StateT
這樣的層。 這也正是什么MFunctor
的情況下mmorph
自動做。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.