从新创建的monad执行monadic代码

时间:2013-11-03 14:42:11

标签: haskell monads state-monad lifting

我目前有两个共享相同类型的monad,实现类似于State monad:

newtype FooRead  a = FooRead  { runFooRead  :: Context -> (a,Context) }
newtype FooWrite a = FooWrite { runFooWrite :: Context -> (a,Context) }

它们之间的区别在于第一个只允许读取上下文(bind不会改变它),而第二个允许编辑上下文。

然后有一些函数使用FooRead中的上下文来计算某个值而不改变上下文的状态:

getVal :: FooRead a
getVal = do x <- ...
            return x

现在我想从编写器monad中的代码执行这些读取函数之一:

writeFunc :: FooWrite ()
writeFunc = do x <- liftVal getVal
            ...

其中liftVal :: FooRead a -> FooWrite a是一个提取FooRead函数返回的值并将其转换为FooWrite monad的函数。这一切都很好。

但是,我无法找到一种方法将上面getVal的执行从我的FooWrite monad推送到上下文中。通过上面的实现,getVal将在monad的空实例中运行。

我可以弄清楚如何使用FooRead上下文构建FooWrite实例

lower :: FooWrite a -> FooRead a

基本上我想把我的作家降级为读者,在读者中执行代码,然后将其重新推广给作家。

但不是如何在这个monad中实际执行代码?

1 个答案:

答案 0 :(得分:1)

以下是我如何实现这一点。首先,如果您希望WriterReader更强大,而不是我们想要的功能

liftReader :: FooReader a -> FooWriter a
liftReader (FooReader a) = FooWriter a

这是有效的,因为它们在结构上是等价的,它们的monad实例应该是同构的。

然后我们可以

t :: FooWriter Int
t = liftReader getVal

如果你想能够走另一条路,那就很容易了

liftWriter :: FooWriter a -> FooReader a
liftWriter (FooWriter a) = FooReader a

现在我们可以将这些类型相互提升的事实使你认为它们在某种程度上是等价的。事实上它们是,你基本上已经

import Control.Monad.State
newtype FooReader s a = FooReader (State s a)
newtype FooWriter s a = FooWriter (State s a)

State提供的getput类似于您的getValwriteFunc