我目前有两个共享相同类型的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中实际执行代码?
答案 0 :(得分: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
提供的get
和put
类似于您的getVal
和writeFunc