我如何使用 (->) r Monad 或其他 (->) r type class 实例在这里可能有用来简化函数应用程序?

How can I simplify function application with (->) r Monad or which other (->) r type class instance may be useful here?

例如我有以下代码:

func1 :: a -> b
func3 :: b -> b -> b -> c

myExample x y z = func3 (func1 x) (func1 y) (func1 z)

对这种情况有什么一般性建议吗?

Monad 的 (->) r 实例可以被认为是向(多个)函数提供 "supply of r"。它的使用示例可能是:

example f g h x = f (g x) (h x)

会变成

example' f g h = do
  x1 <- g
  x2 <- h
  return (f x1 x2)

其中参数(最初 x)被提供给每个函数。

您的示例没有为多个函数提供 r 的单一供应,而是为单个函数提供多个 r 值。这不是 Monad 的 (->) r 实例的用例。

(->) r monad 并不是真的"simplify application."应用函数最简单的方法就是应用函数!

我发现感受 (->) r 的最直观方式是它允许您 对共享相同参数类型的函数的结果类型进行操作 .比如说你有这些功能:

f :: a -> b -> c
g :: r -> a
h :: r -> b

在这种情况下,请注意:

  1. fgh 的结果类型进行操作。
  2. gh 共享相同的参数类型。

现在为 (->) r 使用 Applicative 实例(Monad 的超类),你可以这样写:

import Control.Applicative (liftA2)

example :: r -> c
example = liftA2 f g h

因此,如果您有许多函数都采用类型为 r 的共享 "context" 参数,并且您的代码到处都是线程,有时您可以通过使用 Reader r monad(这只是 (->) r 周围的 newtype)编写代码,"pretends" 已经提供了上下文值。


扩展我对这个答案的第一条评论,考虑到你到目前为止告诉我们的关于你的例子的内容,我没有看到使用任何这种机器的情况。在这个定义中:

myExample x y z = func3 (func1 x) (func1 y) (func1 z)

...func1 应用于三个不同的值,因此以下内容不等价:

notYourExample = liftA3 func3 func1 func1 func1

这相当于:

notYourExample x = func3 (func1 x) (func1 x) (func1 x)

函数中重复的不是 func1 的参数,而是 func1 本身。您可以使用 (->) r monad 通过应用一些代码 golf:

来消除重复
myExample x y z = liftA3 func3 ($x) ($y) ($z) func1

你觉得比原著难读吗?好吧,这就是不这样做的原因!它也可能不严格等同于您的示例,具体取决于 func1.

的精确类型