如何使用 Monad 的 (->) 实例以及关于 (->) 的困惑
在不同的问题上,我在评论中找到了有关使用 Monad 的 (->)
实例的提示,例如用于实现无点样式。
对于我来说,这有点太抽象了。好的,我在 (->)
上看到了 Arrow 实例,在我看来, (->)
可以在实例符号中使用,但不能在类型中使用声明(这本身就是另一个问题的内容)。
有没有人使用 (->)
作为 Monad 实例的例子?或者一个好的链接?
抱歉,如果这个问题可能已经在这里讨论过,但是搜索“(->)
Monad实例”会给你带来很多你可以想象的命中......因为几乎每个关于 Haskell 的问题都涉及 (->)
或“Monad”。
At different questions I've found hints in comments concerning using the (->)
instance of Monads e.g. for realizing point-free style.
As for me, this is a little too abstract. Ok, I've seen Arrow instances on (->)
and it seems to me, that (->)
can be used in instance notations but not in type declarations (that would alone be stuff for another question).
Has anyone examples using (->)
as instance of Monad? Or a good link?
Sorry if this question may already have been discussed here, but searching for "(->)
Monad instance" gives you many many hits as you can imagine ... since nearly every question about Haskell somewhere involves (->)
or "Monad".
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
对于给定类型
r
,类型r -> 的函数a
可以被认为是使用类型为r
的环境提供a
的计算。给定两个函数r -> a
和a -> (r -> b)
,很容易想象,当给定一个环境(同样,类型为r
)时,人们可以组合这些。但是等等!这正是 monad 的意义所在!
因此,我们可以为
(->) r
(*) 创建一个 Monad 实例,通过传递r
来实现f >>= g
code> 为f
和g
。这就是(->) r
的 Monad 实例所做的事情。要实际访问环境,您可以使用
id :: r -> r
,您现在可以将其视为在环境r
中运行并提供r
的计算。要创建本地子环境,您可以使用以下方法:这种将环境传递给计算的模式,然后可以在本地查询并修改它,这不仅适用于
(->) r
monad,这就是为什么它被抽象为MonadReader
类,使用比我在这里使用的更明智的名称:http://hackage.haskell.org/packages/archive/mtl/2.0.1.0/doc /html/Control-Monad-Reader-Class.html
基本上,它有两个实例:我们在这里看到的
(->) r
和ReaderT r m< /code>,它只是一个
r -> 的
,所以它与我在这里描述的newtype
包装器。 m a(->) r
monad 是一样的,只不过它在其他一些转换后的 monad 中提供计算。(*)
(->) r 乍一看可能很有趣,但这是 Haskell 语法,用于部分地将两参数类型构造函数
(->)
应用到第一个类型参数r
,即它的含义类似于r -> _
第二个参数尚未给出。因此(->) r a
(或((->) r) a
)与r ->;一个。
For a given type
r
, the function of typer -> a
can be thought of as a computation delivering ana
using an environment typedr
. Given two functionsr -> a
anda -> (r -> b)
, it's easy to imagine that one can compose these when given an environment (again, of typer
).But wait! That's exactly what monads are about!
So we can create an instance of Monad for
(->) r
(*) that implementsf >>= g
by passing ther
to bothf
andg
. This is what the Monad instance for(->) r
does.To actually access the environment, you can use
id :: r -> r
, which you can now think of as a computation running in an environmentr
and delivering anr
. To create local sub-environments, you can use the following:This pattern of having an environment passed to computations that can then query it and modify it locally is useful for not just the
(->) r
monad, which is why it is abstracted into theMonadReader
class, using much more sensible names than what I've used here:http://hackage.haskell.org/packages/archive/mtl/2.0.1.0/doc/html/Control-Monad-Reader-Class.html
Basically, it has two instances:
(->) r
that we've seen here, andReaderT r m
, which is just anewtype
wrapper aroundr -> m a
, so it's the same thing as the(->) r
monad I've described here, except it delivers computations in some other, transformed monad.(*)
(->) r
might look funny at first, but this is Haskell syntax for partially applying the two-parameter type constructor(->)
to the first type argumentr
, i.e. it means something liker -> _
with the second argument not yet given. So(->) r a
(or((->) r) a
) is the same asr -> a
.要为
(->) r
定义一个 monad,我们需要两个操作,return
和(>>=)
,具体取决于三个定律:如果我们查看
(->) r
的 return 签名,我们可以看到它只是一个常量函数,它忽略了它的第二个参数。
或者,
要构建
(>>=)
,如果我们使用 monad(->) r
专门化其类型签名,实际上只有一个可能的定义。
使用这个 monad 就像向每个函数传递一个额外的参数
r
。您可以使用它进行配置,或者将选项深入到程序的内部。我们可以通过验证三个单子定律来检查它是否是一个单子:
最终的单子定律:
遵循类似、简单的等式推理。
我们还可以为 ((->) r) 定义许多其他类,例如 Functor,
如果我们查看 的签名,
我们可以看到它只是组合!
类似地,我们可以创建一个 Applicative 实例
拥有这些实例的好处是它们可以让您使用所有 Monad 和 Applicative 操作函数时的组合器。
有很多涉及 (->) 的类实例,例如,您可以手写 Monoid for (b -> a),给定
a
上的 Monoid 为:但给定 Monad/Applicative例如,您还可以
使用
(->) r
的 Applicative 实例或使用
(->) r
的 Monad 实例来定义此实例。这里节省的空间很小,但是,例如 #haskell IRC 频道上的 lambdabot 提供的用于生成无点代码的 @pl 工具就滥用了这些实例。
To define a monad for
(->) r
, we need two operations,return
and(>>=)
, subject to three laws:If we look at the signature of return for
(->) r
we can see its just the constant function, which ignores its second argument.
Or alternately,
To build
(>>=)
, if we specialize its type signature with the monad(->) r
,there is really only one possible definition.
Using this monad is like passing along an extra argument
r
to every function. You might use this for configuration, or to pass options way down deep into the bowels of your program.We can check that it is a monad, by verifying the three monad laws:
The final monad law:
follows by similar, easy equational reasoning.
We can define a number of other classes for ((->) r) as well, such as Functor,
and if we look at the signature of
we can see that its just composition!
Similarly we can make an instance of
Applicative
What is nice about having these instances is they let you employ all of the Monad and Applicative combinators when manipulating functions.
There are plenty of instances of classes involving (->), for instance, you could hand-write the instance of Monoid for (b -> a), given a Monoid on
a
as:but given the Monad/Applicative instance, you can also define this instance with
using the Applicative instance for
(->) r
or withusing the Monad instance for
(->) r
.Here the savings are minimal, but, for instance the @pl tool for generating point-free code, which is provided by lambdabot on the #haskell IRC channel abuses these instances quite a bit.