关于WriterT monad值的构成?

时间:2014-09-19 22:22:00

标签: haskell

让我们有一个函数返回以下类型的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) .)

但显然不符合要求。

任何人都可以帮助实施或至少显示正确的路径吗?

感谢。

2 个答案:

答案 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为IOMonadBaseControl允许您将许多不同的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