何时以及为什么要在Scala中使用Applicative Functors [英] When and why should one use Applicative Functors in Scala
问题描述
我知道 Monad
可以在Scala中表示如下:
<$ c $ (F:A => F [B]):F [A] => F [B]
}
我明白为什么它很有用。例如,给定两个函数:
$ $ p $ $ $ c $ getUserById(userId:Int):Option [User] = ...
getPhone(user:User):Option [Phone] = ...
code> getPhoneByUserId(userId:Int)既然选项
是一个单子:
def getPhoneByUserId(userId:Int):Option [Phone] =
getUserById(userId).flatMap(user => getPhone(user))
...
现在我看到 Scala中的Applicative Functor
:
trait Applicative [F [_]] {
def应用[A,B](f:F [A => B]):F [A] => F [B]
}
我不知道何时应该使用它而不是 monad。我想Option和List都是 Applicatives
。你可以举个简单的例子,在选项和列表中使用 apply >,并解释为什么我应该使用而不是
flatMap
?
那么为什么还要用到应用函子呢?有单子吗?
首先,为
提供monad实例根本不可能,我们想要使用的一些抽象 -Validation
是$ b $一个完美的例子。
第二个(也是相关的),使用
最强大的抽象将完成工作只是一个可靠的开发实践。在
原则中,这可能会使优化不会成为
的可能,但更重要的是,它使得我们编写的代码更多
可重用。
在第一段中扩展一下:有时你在单向代码和应用代码之间没有选择。请参阅回答的其余部分,了解您为什么要使用Scalaz的验证
(它不能也没有monad实例)来建模
验证。
关于优化点:它会可能会有一段时间,这通常与Scala或Scalaz有关,但请参阅 Haskell的 Data.Binary
的文档:
应用风格有时会导致代码更快,因为
binary
会尝试通过将读取组合在一起来优化代码。
编写应用程序代码可以避免对计算之间的依赖关系做出不必要的声明 - 声称类似的一元代码会提交给您。一个足够智能的库或编译器可以原则上利用这一事实。
为了让这个想法更具体一些,请考虑以下monadic代码: case class Foo(s:Symbol,n:Int)
val maybeFoo = for {
s< - maybeComputeS(无论)
n< - maybeComputeN(无论)
}产生Foo(s,n)
-comprehension的解析为如下所示的内容:
val maybeFoo = maybeComputeS(whatever).flatMap(
s => maybeComputeN(whatever).map(n => Foo(s,n))
)
我们知道 应用版本(使用Scalaz)如下所示: 这里我们明确指出:这两个计算之间没有依赖关系。 但最后一点确实是最重要的。挑选能够解决您的问题的至少强大的工具是一个非常强大的原则。例如,有时你确实需要monadic组合,例如在你的 遗憾的是,Haskell和Scala目前使单子组合的工作变得比使用适用函子更方便(语法等),但这主要是历史事件的问题,而像 idiom brackets 是朝着正确方向迈出的一步。 I know that I see why it is useful. For example, given two functions: I can easily write function ... Now I see I wonder when I should use it instead of monad . I guess both Option and List are 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— 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. 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 answer for a discussion of why you might want to use Scalaz's 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 The applicative style can sometimes result in faster code, as 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 could in principle take advantage of this fact. To make this idea a little more concrete, consider the following monadic code: The We know that The applicative version (using Scalaz) looks like this: Here we're explicitly stating that there's no dependency between the two computations. (And yes, this The last point is really the most important, though. Picking the least powerful tool that will solve your problem is a tremendously powerful principle. Sometimes you really do need monadic composition—in your 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 brackets are a step in the right direction. 这篇关于何时以及为什么要在Scala中使用Applicative Functors的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋! maybeComputeN(whatever)
并不依赖于 s
(假设这些行为良好的方法不会改变幕后的某些可变状态),但编译器不会 - 从它的角度来看,它需要知道 s
,才能开始计算 n <
val maybeFoo =(maybeComputeS(whatever)| @ | maybeComputeN(whatever))(Foo(_,_))
(是的,这个 | @ |
语法非常可怕 - 参见< a href =http://meta.plasm.us/posts/2013/06/05/applicative-validation-syntax/ =noreferrer>这篇博文有一些讨论和选择。)
getPhoneByUserId
方法中,但通常你不需要。
Monad
can be expressed in Scala as follows:trait Monad[F[_]] {
def flatMap[A, B](f: A => F[B]): F[A] => F[B]
}
getUserById(userId: Int): Option[User] = ...
getPhone(user: User): Option[Phone] = ...
getPhoneByUserId(userId: Int)
since Option
is a monad:def getPhoneByUserId(userId: Int): Option[Phone] =
getUserById(userId).flatMap(user => getPhone(user))
Applicative Functor
in Scala:trait Applicative[F[_]] {
def apply[A, B](f: F[A => B]): F[A] => F[B]
}
Applicatives
. Could you give simple examples of using apply
with Option and List and explain why I should use it instead of flatMap
?
Validation
is the
perfect example.Validation
(which doesn't and can't have a monad instance) to model
validation.Data.Binary
:
binary
will try to optimize the code by grouping the reads together.case class Foo(s: Symbol, n: Int)
val maybeFoo = for {
s <- maybeComputeS(whatever)
n <- maybeComputeN(whatever)
} yield Foo(s, n)
for
-comprehension desugars to something more or less like the following:val maybeFoo = maybeComputeS(whatever).flatMap(
s => maybeComputeN(whatever).map(n => Foo(s, n))
)
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 s
before it can start computing n
.val maybeFoo = (maybeComputeS(whatever) |@| maybeComputeN(whatever))(Foo(_, _))
|@|
syntax is pretty horrible—see this blog post for some discussion and alternatives.)getPhoneByUserId
method, for example—but often you don't.