Haskell 读者monad,bind读者是如何工作的?
我是哈斯克尔的初学者。我学习了如何创建阅读器以及如何查询共享变量。我查看了Hugs98中Reader.hs的源代码Haskell 读者monad,bind读者是如何工作的?,haskell,monads,Haskell,Monads,我是哈斯克尔的初学者。我学习了如何创建阅读器以及如何查询共享变量。我查看了Hugs98中Reader.hs的源代码 instance Monad (Reader r) where return a = Reader $ \_ -> a m >>= k = Reader $ \r -> runReader (k (runReader m r)) r 这里我可以看到(returna)创建了一个读取器,它包装了一个函数,该函数接受一个
instance Monad (Reader r) where
return a = Reader $ \_ -> a
m >>= k = Reader $ \r ->
runReader (k (runReader m r)) r
这里我可以看到(returna)创建了一个读取器,它包装了一个函数,该函数接受一个值并返回一个值
m>>=k是我无法理解的。首先,这是如何应用的?也许两个读者绑定的例子能有所帮助
其次,对我来说,执行是多么的模糊
我不明白将k应用于(runReader m r)的结果有什么意义
谢谢好的,让我们看看
m>=k
。这里,m
是读卡器,k
是产生读卡器的函数。那么这有什么用呢
runReader m r
好的,这是运行m
,将r
作为要读取的输入
k (runReader m r)
这将获取运行m
的输出,并将其传递给k
。这使得k
返回另一个读卡器
这将获取由k
返回的读取器并运行该读取器(使用相同的输入r
进行读取)
你明白了吗?好的,让我们看看
m>=k
。这里,m
是读卡器,k
是产生读卡器的函数。那么这有什么用呢
runReader m r
好的,这是运行m
,将r
作为要读取的输入
k (runReader m r)
这将获取运行m
的输出,并将其传递给k
。这使得k
返回另一个读卡器
这将获取由k
返回的读取器并运行该读取器(使用相同的输入r
进行读取)
你明白了吗?读卡器的定义如下:
newtype Reader r a = Reader { runReader :: r -> a }
所以它实际上只是一个r->a
类型的函数,带有一些额外的封装。这是有道理的,因为读者实际上只是为monad中的所有操作提供了一个额外的输入
如果我们去掉封装,只使用r->a
函数,则一元函数的类型为:
return :: a -> (r -> a) -- or: a -> r -> a
(>>=) :: (r -> a) -> (a -> (r -> b)) -> (r -> b) -- or: (r -> a) -> (a -> r -> b) -> r -> b
从这一点来看,我们更容易看到我们需要什么。如果您查看类型a->(r->a)
,并看到这相当于a->r->a
,您可以从两个方面查看此函数。一个是取一个a
参数并返回一个r->a
类型的函数,另一个是将is视为一个函数,它取一个a
和一个r
并返回一个a
。您可以使用以下任一视图实现返回:
return a = \r -> a -- or: return a r = a
绑定更为棘手,但同样的逻辑也适用。在我给出的第一个类型签名中,类型中的第三个r
实际上也是一个输入,而第二个类型签名使这一点非常容易看到。因此,让我们从实现第二种类型签名开始:
(>>=) rToA aAndRToB r = ...
所以我们有一个r
类型的值,一个r->a
类型的函数和一个a->r->b
类型的函数,我们的目标是从中得到一个b
类型的值。我们输入的唯一b
是a->r->b
函数,因此我们需要使用它,但我们没有a
来输入它,所以我们需要一个。如果我们有一个r
函数,那么r->a
函数可以提供一个。我们有一个r
,这是我们的第三个输入。因此,我们可以简单地应用函数,直到得到b
:
(>>=) rToA aAndRToB r = b where
a = rToA r
b = aAndRToB a r
在这里,您可以看到,我们为每个动作提供r
-值(这是读卡器monad的目标),同时还将a
-值从一个动作链接到下一个动作(这是(>>=)
)的目标)。您也可以模仿第一个类型签名的方式编写此文件,如下所示:
(>>=) rToA aToRToB = \r -> (aToRToB (rToA r)) r
如果重命名变量,看起来与读卡器绑定的定义非常相似,但不使用Reader
和runReader
:
m >>= k = /r -> k (m r) r
读卡器的定义如下:
newtype Reader r a = Reader { runReader :: r -> a }
所以它实际上只是一个r->a
类型的函数,带有一些额外的封装。这是有道理的,因为读者实际上只是为monad中的所有操作提供了一个额外的输入
如果我们去掉封装,只使用r->a
函数,则一元函数的类型为:
return :: a -> (r -> a) -- or: a -> r -> a
(>>=) :: (r -> a) -> (a -> (r -> b)) -> (r -> b) -- or: (r -> a) -> (a -> r -> b) -> r -> b
从这一点来看,我们更容易看到我们需要什么。如果您查看类型a->(r->a)
,并看到这相当于a->r->a
,您可以从两个方面查看此函数。一个是取一个a
参数并返回一个r->a
类型的函数,另一个是将is视为一个函数,它取一个a
和一个r
并返回一个a
。您可以使用以下任一视图实现返回:
return a = \r -> a -- or: return a r = a
绑定更为棘手,但同样的逻辑也适用。在我给出的第一个类型签名中,类型中的第三个r
实际上也是一个输入,而第二个类型签名使这一点非常容易看到。因此,让我们从实现第二种类型签名开始:
(>>=) rToA aAndRToB r = ...
所以我们有一个r
类型的值,一个r->a
类型的函数和一个a->r->b
类型的函数,我们的目标是从中得到一个b
类型的值。我们输入的唯一b
是a->r->b
函数,因此我们需要使用它,但我们没有a
来输入它,所以我们需要一个。如果我们有一个r
函数,那么r->a
函数可以提供一个。我们有一个r
,这是我们的第三个输入。因此,我们可以简单地应用函数,直到得到b
:
(>>=) rToA aAndRToB r = b where
a = rToA r
b = aAndRToB a r
在这里,您可以看到,我们为每个动作提供r
-值(这是读卡器monad的目标),同时还将a
-值从一个动作链接到下一个动作(这是(>>=)
)的目标)。您也可以用模仿第一个类型签名的方式编写它
-- General monad
(>>=) :: m a -> (a -> m b) -> m b
-- Reader monad
(>>=) :: Reader r a -> (a -> Reader r b) -> Reader r b
-- Let me put it in a (syntactically incorrect, but) more illustrative form
(>>=) :: (Reader r -> a) -> (a -> (Reader r -> b)) -> (Reader r -> b)
-- A reader is a function of type (r -> a), packed into a Reader context.
-- If we want to access the wrapped function, we can easily do it with runReader.
-- With this in mind, let's see how it would be without the extra context.
(>>=) :: (r -> a) -> (a -> (r -> b)) -> (r -> b)
-- Takes m and f, returns result
m >>= f = result
-- The types
m :: r -> a
f :: a -> (r -> b)
result :: r -> b
-- Implementation
m >>= f = \x -> (f (m x)) x
-- A quick How-We-Got-Here
mResult = m x -- :: a
fResult = f mResult -- :: r -> b
result = \x -> fResult x
= \x -> (f mResult) x
= \x -> (f (m x)) x
-- The types
m :: Reader (r -> a)
f :: a -> Reader (r -> b)
result :: Reader (r -> b)
-- Functions we easily used before, are now in a "Reader".
-- But we can easily unwrap and access them with "runReader".
-- Now "result" is not just a function, but one in a "Reader".
-- But we can easily wrap it with "Reader".
-- Apply these tools on our How-We-Got-Here analogy from before.
mResult = (runReader m) x -- :: a
fResult = f mResult -- :: Reader (r -> b)
result = Reader $ \x -> (runReader fResult) x
= Reader $ \x -> (runReader (f mResult)) x
= Reader $ \x -> (runReader (f ((runReader m) x))) x
m >>= f = Reader $ \x -> (runReader (f ((runReader m) x))) x
-- Remove the unnecessary parens
m >>= f = Reader $ \x -> runReader (f (runReader m x)) x
-- Different letters
m >>= k = Reader $ \r -> runReader (k (runReader m r)) r