何时以及为什么应该在 Scala 中使用 Applicative Functors
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19880207/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
When and why should one use Applicative Functors in Scala
提问by Michael
I know that Monadcan be expressed in Scala as follows:
我知道Monad可以用 Scala 表示如下:
trait Monad[F[_]] {
def flatMap[A, B](f: A => F[B]): F[A] => F[B]
}
I see why it is useful. For example, given two functions:
我明白为什么它很有用。例如,给定两个函数:
getUserById(userId: Int): Option[User] = ...
getPhone(user: User): Option[Phone] = ...
I can easily write function getPhoneByUserId(userId: Int)since Optionis a monad:
我可以轻松编写函数,getPhoneByUserId(userId: Int)因为它Option是一个 monad:
def getPhoneByUserId(userId: Int): Option[Phone] =
getUserById(userId).flatMap(user => getPhone(user))
...
...
Now I see Applicative Functorin Scala:
现在我Applicative Functor在 Scala 中看到:
trait Applicative[F[_]] {
def apply[A, B](f: F[A => B]): F[A] => F[B]
}
I wonder when I should use it instead ofmonad . I guess both Option and List are Applicatives. Could you give simple examples of using applywith Option and List and explain whyI should use it instead offlatMap?
我想知道什么时候应该使用它而不是monad 。我猜 Option 和 List 都是Applicatives. 您能否给出使用applyOption 和 List 的简单示例并解释为什么我应该使用它而不是flatMap?
回答by Travis Brown
To quote myself:
要引用自己:
So why bother with applicative functors at all, when we've got monads? First of all, it's simply not possible to provide monad instances for some of the abstractions we want to work with—
Validationis the perfect example.Second (and relatedly), it's just a solid development practice to use the least powerful abstraction that will get the job done. In principle this may allow optimizations that wouldn't otherwise be possible, but more importantly it makes the code we write more reusable.
那么,当我们有 monad 时,为什么还要为 applicative functor 烦恼呢?首先,根本不可能为我们想要使用的一些抽象提供 monad 实例——这
Validation是一个完美的例子。其次(并且相关地),使用最不强大的抽象来完成工作只是一种可靠的开发实践。原则上,这可能允许进行原本不可能进行的优化,但更重要的是,它使我们编写的代码更具可重用性。
To expand a bit on the first paragraph: sometimes you don't have a choice between monadic and applicative code. See the rest of that answerfor a discussion of why you might want to use Scalaz's Validation(which doesn't and can't have a monad instance) to model
validation.
对第一段进行一点扩展:有时您无法在 monadic 和 applicative 代码之间进行选择。请参阅该答案的其余部分以讨论为什么您可能想要使用 Scalaz Validation(它没有也不能有 monad 实例)来进行模型验证。
About the optimization point: it'll probably be a while before this is generally relevant in Scala or Scalaz, but see for example the documentation for Haskell's Data.Binary:
关于优化点:这可能需要一段时间才能在 Scala 或 Scalaz 中普遍相关,但例如参见Haskell 的文档Data.Binary:
The applicative style can sometimes result in faster code, as
binarywill try to optimize the code by grouping the reads together.
applicative 风格有时会产生更快的代码,因为
binary将尝试通过将读取分组在一起来优化代码。
Writing applicative code allows you to avoid making unnecessary claims about dependencies between computations—claims that similar monadic code would commit you to. A sufficiently smart library or compiler couldin principle take advantage of this fact.
编写应用代码可以让你避免对计算之间的依赖做出不必要的声明——类似的 monadic 代码会让你做出这样的声明。一个足够聪明的库或编译器可能在这个事实的原则乘虚而入。
To make this idea a little more concrete, consider the following monadic code:
为了使这个想法更具体一点,请考虑以下 monadic 代码:
case class Foo(s: Symbol, n: Int)
val maybeFoo = for {
s <- maybeComputeS(whatever)
n <- maybeComputeN(whatever)
} yield Foo(s, n)
The for-comprehension desugars to something more or less like the following:
该for-comprehension desugars的东西或多或少这样的:
val maybeFoo = maybeComputeS(whatever).flatMap(
s => maybeComputeN(whatever).map(n => Foo(s, n))
)
We know that maybeComputeN(whatever)doesn't depend on s(assuming these are well-behaved methods that aren't changing some mutable state behind the scenes), but the compiler doesn't—from its perspective it needs to know sbefore it can start computing n.
我们知道这maybeComputeN(whatever)不依赖s(假设这些是行为良好的方法,不会在幕后改变某些可变状态),但编译器不依赖——从它的角度来看,它需要知道s才能开始计算n。
The applicative version (using Scalaz) looks like this:
应用版本(使用 Scalaz)如下所示:
val maybeFoo = (maybeComputeS(whatever) |@| maybeComputeN(whatever))(Foo(_, _))
Here we're explicitly stating that there's no dependency between the two computations.
在这里,我们明确指出两个计算之间没有依赖关系。
(And yes, this |@|syntax is pretty horrible—see this blog postfor some discussion and alternatives.)
(是的,这种|@|语法非常糟糕——有关一些讨论和替代方案,请参阅此博客文章。)
The last point is really the most important, though. Picking the leastpowerful tool that will solve your problem is a tremendously powerful principle. Sometimes you really do need monadic composition—in your getPhoneByUserIdmethod, for example—but often you don't.
不过,最后一点确实是最重要的。选择最不强大的工具来解决您的问题是一个非常强大的原则。有时你确实需要一元组合——getPhoneByUserId例如在你的方法中——但通常你不需要。
It's a shame that both Haskell and Scala currently make working with monads so much more convenient (syntactically, etc.) than working with applicative functors, but this is mostly a matter of historical accident, and developments like idiom bracketsare a step in the right direction.
遗憾的是,Haskell 和 Scala 目前都使使用 monad 比使用应用函子更方便(在语法等方面),但这主要是历史事故,而像方括号这样的发展是正确的一步方向。
回答by Yuriy
Functor is for lifting computations to a category.
Functor 用于将计算提升到一个类别。
trait Functor[C[_]] {
def map[A, B](f : A => B): C[A] => C[B]
}
And it works perfectly for a function of one variable.
它非常适用于一个变量的函数。
val f = (x : Int) => x + 1
But for a function of 2 and more, after lifting to a category, we have the following signature:
但是对于 2 及以上的函数,提升到一个类别后,我们有以下签名:
val g = (x: Int) => (y: Int) => x + y
Option(5) map g // Option[Int => Int]
And it is the signature of a applicative functor. And to apply the?following value to a function g— an aplicative functor is needed.
它是应用函子的签名。并将下面的值应用到一个函数g——需要一个 aplicative functor。
trait Applicative[F[_]] {
def apply[A, B](f: F[A => B]): F[A] => F[B]
}
And finally:
最后:
(Applicative[Option] apply (Functor[Option] map g)(Option(5)))(Option(10))
Applicative functor is a functor for applying a special value (value in category) to a lifted function.
Applicative functor 是用于将特殊值(类别中的值)应用于提升函数的函子。

