1

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

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

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

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

whereliftVal :: FooRead a -> FooWrite a是一个函数,它提取函数返回的值并将其FooRead滚动到FooWritemonad 中。这一切都很好。

但是,我想不出一种方法将getVal上面的执行从我的FooWritemonad 滚动到上下文中。通过上面的实现,getVal将运行在一个空的 monad 实例中。

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

lower :: FooWrite a -> FooRead a

本质上,我想将我的作者降级为读者,在阅读器中执行代码,然后将其重新提升为作者。

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

4

1 回答 1

1

以下是我将如何实现这一点。首先,如果你想比我们想要的功能Writer更强大Reader

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您的getVal 和类似的writeFunc

于 2013-11-03T15:05:09.970 回答