newtype Reader e a = R { runReader :: e -> a }
instance Monad (Reader e) where
return a = R $ _ -> a
m >>= k = R $ r -> runReader (k (runReader m r)) r
我很难理解这两个片段。我可以说第一个是读取器的记录语法描述,具有从e
到a
runReader
的功能,但第二个让我感到困惑。
通过将m
与k
绑定,它本质上是试图创建一个新的Reader
,但是如何
runReader (k (runReader m r)) r
制定出?我以为runReader
只需要一个论点,但现在似乎需要两个论点,一个是k (runReader m r)
,另一个是r
。
提前谢谢。
我很难理解阅读器Monad。
编辑: 我应该指出一些这方面的资源,而不是试图重复它们。
- Monads for Functional Programming byPhilip Wadler.他称他们为国家读者monads。
- 第 22.1 章:克里斯·艾伦和朱莉·莫罗努基的 HaskellBook.com 读者。
- Typeclassopedia:Monad Instances 改编自 Brent Yorgey 的 The Monad.Reader 第 13 期。
- 利亚 第13章多几个
我以为
runReader
只需要一个论点,但现在似乎需要两个论点,一个是k (runReader m r)
,另一个是r
。
如果你看一下runReader :: Reader e a -> e -> a
的类型签名,这可以看作是取一个Reader e a
并产生一个e -> a
,或者看成一个Reader e a
和一个e
并产生一个a
。读者 monad 的重点是引入一个隐含的论点。
runReader (k (runReader m r)) r
如何解决?
您可以进一步说明绑定运算符的定义:
instance Monad (Reader e) where
return a = R $ _ -> a
-- (>>=) :: Reader e a -> (a -> Reader e b) -> Reader e b
ma >>= k = R $ e -> let a = runReader ma e
mb = k a
in runReader mb e
也就是说,首先"ma
用e
运行"(runReader ma :: e -> a
应用于e
)。这将产生一个a
。
然后运行k a
。这将产生一个mb
。
然后"mb
与e
一起运行"(runReader mb :: e -> b
应用于e
)。
这被打包成R $ e -> ... runReader mb e
.
我开始认为,理解这一点的困难部分主要与newtype需要不断包装(R
)和解包(runReader
)的方式有关,而不是臭名昭著的monads如何工作。
想象一下,你唯一需要的monad是阅读器monad,我们可以没有newtype
和instance Monad (Reader e)
绒毛。那么你的定义可能看起来像这样:
type Reader e a = e -> a
-- type Reader e a = (->) e a
-- type Reader e = (->) e
unit :: a -> Reader e a
-- :: a -> (e -> a)
unit a = _e -> a
-- unit a _e = a
-- unit = const
ask :: Reader e e
-- :: e -> e
ask = e -> e
-- ask e = e
-- ask = id
bind :: Reader e a -> (a -> Reader e b) -> Reader e b
-- :: (e -> a) -> (a -> (e -> b)) -> (e -> b)
bind ma k = e -> let a = ma e
mb = k a
in mb e
-- bind ma k e = let mb = k (ma e) in mb e
在这一点上,更清楚的是,unit
所做的只是丢弃e
,ask
所做的只是返回e
,而bind
所做的是接受两个都期望e
的函数(ma
和k (ma e)
,又名mb
),将它们组合在一个也期望e
的新函数中, 无需在合成过程中显式传递e
。
在学习如何编写monad定义时,我自己有一个误解,即runReader
运行任何东西。它在概念上帮助我称它为unR
因为它所做的只是删除R
包装。