让我们有一个函数返回以下类型的monad writer:
getWriter :: a -> WriterT w IO a
现在我在撰写以下签名的作文功能时遇到了一些麻烦:
composeWAct :: (IO a -> IO a) -> (a -> WriterT w IO a) -> a -> WriterT w IO a
第一个参数IO a -> IO a
旨在应用于WriterT的monadic值。
使用示例:composeWAct (print "Hello !" >>) getWriter
我正在寻找一种成分解决方案 - 即。 没有需要运行/评估作者。
到目前为止,我尝试使用标准模块Control.Monad和Control.Monad.Writer实现composeWAct
之类的功能:
composeWAct' iofn = ((ap . liftIO) (iofn return id) .)
但显然不符合要求。
任何人都可以帮助实施或至少显示正确的路径吗?
感谢。
答案 0 :(得分:3)
库函数的类型略有不同,可以满足您的需求:来自hoist
包的mmorph
。它的类型可以专门用于(forall a. IO a -> IO a) -> WriterT w IO b -> WriterT w IO b
。它的构成与您想象的功能略有不同。 (我不确定你的意思是不需要运行/评估作者。如果你关心它的内部工作,请看hoist
WriterT
的实现。)
这是一个经过深思熟虑的例子。
prefixHello :: IO a -> IO a
prefixHello m = print "Hello" >> m
myWriter :: a -> WriterT String IO a
myWriter x = tell "logging info" >> return x
wtcomp :: WriterT String IO String
wtcomp = hoist prefixHello $ myWriter "value"
ghci> runWriterT $ wtcomp
"Hello"
("value","logging info")
答案 1 :(得分:1)
作为@ ChristianConkle方法的替代方法,您可以使用MonadBaseControl
,它的设计确实可以让您访问monad堆栈的底层基础monad。在大多数情况下,基本monad为IO
,MonadBaseControl
允许您将许多不同的IO
函数(例如fork
)提升为基于IO
的函数monad stack(请参阅lifted-base包)。
Control.Monad.Trans.Control
中有几个可以直接用于您的任务的实用程序功能。特别是,以下两个可以专门用作:
{-# LANGUAGE RankNTypes #-}
import Control.Monad.Trans.Control
liftBaseOp' :: (MonadBaseControl b m)
=> (forall c . (a -> b c) -> b c) -> (a -> m d) -> m d
liftBaseOp' f = liftBaseOp f
liftBaseOp_' :: (MonadBaseControl b m)
=> (forall a . b a -> b a) -> m c -> m c
liftBaseOp_' f = liftBaseOp_ f
所以你可以写
wtcomp :: WriterT String IO String
wtcomp = liftBaseOp_ prefixHello $ myWriter "value"
优势在于liftBaseOp_
无论monad堆栈有多深,都可以访问IO
。