我知道了莫纳德。阅读器实际上是一个函数的封装,即:
newtype Reader r a = Reader { runReader :: r -> a }
这是一个Monad的例子,
instance Monad (Reader r) where
return a = Reader $ _ -> a
m >>= k = Reader $ r -> runReader (k (runReader m r)) r
相比之下,我知道(->)也是一个Monad,
instance Monad ((->) r) where
return = const
f >>= k = r -> k (f r) r
从定义中可以看出,它们实际上的行为完全相同。
那么,它们在所有用途中都可以互换吗?区别这两个君主的实际意义是什么?
TL;DR
他们是一样的。
一些历史课
State
、Writer
和Reader
的设计灵感来自于Mark p.Jones的重载函数程序设计高阶多态性,他将Reader
定义如下:
Reader
monad用于允许计算访问所保存的值在某些封闭环境中(由下面的类型r
表示定义)。> instance Monad (r->) where > result x = r -> x > x `bind` f = r -> f (x r) r
顺便说一句,有趣的是注意到这两个函数只是组合逻辑的标准CCD_ 7和CCD_。
后来,他定义了(几乎)今天的MonadReader
:
Reader monads
:一类monad,用于描述参考某些固定环境的计算:> class Monad m => ReaderMonad m r where > env :: r -> m a -> m a > getenv :: m r > instance ReaderMonad (r->) r where > env e c = _ -> c e > getenv = id
getenv
是简单的ask
,而env
是local . const
。因此,这个定义已经包含了Reader
的所有重要部分。最终,Jones定义了monad转换器ReaderT
(BComp
是反向合成):
首先,定义两种不同形式的作文是有用的;向前(
FComp
)和向后(BComp
):> data FComp m n a = FC (n (m a)) > data BComp m n a = BC (m (n a))
[省略Functor、Monad和OutOf实例]
> type ReaderT r = BComp (r ->)
由于StateT
、WriterT
和其他类型都有其非变换器变体,因此只有Reader r
才是合乎逻辑的,它实际上与(->) r
相同。
无论哪种方式,现在Reader
、Writer
和State
都是根据它们的转换器变体来定义的,并且您使用它们各自的Monad*
类型类(MonadReader
)。
结论
那么,它们在所有用途中都可以互换吗?
是的。
区别这两个君主的实际意义是什么?
没有,除了ReaderT
实际上是一个monad转换器,这让事情变得更容易。
它们都是MonadReader
类的实例。所以是的,你可以用一个代替另一个。
它们实际上是完全相同的。我们可以通过在它们之间进行映射来使其更加正式:toArrow :: Reader r a -> r -> a
和toReader :: (r -> a) -> Reader r a
实现方式为CCD_ 33和CCD_。
编辑:Reader
背后的语义是,它包含一些只读配置,您可以通过计算链进行处理。当您想要处理一些配置信息时,您应该始终更喜欢Reader
而不是使用纯箭头类型,因为它是一个非常通用的接口的一部分,该接口提供了有用的辅助函数,一个用于操作类似Reader
的数据类型的MonadReader
类,以及一个用于堆叠Monad
s的ReaderT
。