繁体   English   中英

理解 Scala 中的应用函子

[英]Understanding Applicative Functor in Scala

假设我需要编写一些函数来调用一些 REST API: api1api2api3

def api1(url: Url) = ???
def api2(url: Url) = ???
def api3(url: Url) = ???

假设为简单起见,我使用自己的简化类Url

case class Url(host: String, port: Int, path: Path)  

为了构建一个Url我从配置中读取hostport并调用函数api1api2api3 ,它们添加所需的paths并调用它们的 API:

def api1(host: String, port: Int) = ???
def api2(host: String, port: Int) = ???
def api3(host: String, port: Int) = ???

val (host, port) = ... // read from the configuration

// call the APIs
api1(host, port) 
api2(host, port)
api3(host, port)

为了隐藏hostport以及构造Url其他细节,使用函数Path => Url (或builder pattern如果我们用Java编写)要好得多。

def api1(f: Path => Url) = ...
def api2(f: Path => Url) = ...
def api3(f: Path => Url) = ...

很容易实现这样的函数f: Path => Url with curring

val url: String => Int => Path = (Url.apply _).curried
val (host, port) = ... // from the configuration
val f = url(host, port)
api1(f)
api2(f)
api3(f)

到目前为止,一切都很好,但如果有可选的主机和端口怎么办?

val (hostOpt: Option[String], portOpt: Option[Int]) = ... // from configuration 

现在我们有一个函数String => Int => Path => UrlOption[String]Option[Int] 如何获得Path => Url

让我们问一个稍微不同的问题:如何获得Option[Path => Url]给定String => Int => Path => UrlOption[String]Option[Int]

幸运的是,我们可以轻松定义这样的操作:

trait Option[A] { ... def ap[B](of: Option[A => B]): Option[B] = ??? }

鉴于这个ap我们可以回答原来的问题:

 val of: Option[Path => Url] = portOpt ap (hostOpt ap Some(url) 
 of.map(f => api1(f))
 of.map(f => api2(f))
 of.map(f => api3(f))

抽象地说,我们使用了Option是一个应用函子的事实。 如果M是一个函子并且有两个附加操作,则M是一个应用函子:

  • ap得到M[B]给定M[A => B]M[A]
  • pureA => B得到M[A => B] ( Some for Option )

这些操作应该符合两个简单的规律,但这是另一回事。

...

是否有意义 ?

这对我来说听起来很合理,虽然我不确定这里是否有很多问题,这是它自己的问题。

我将其作为答案而不是评论,因为有一件事情值得注意。 对于许多类型,有理由避免 monadic 绑定并坚持使用ap不仅仅是“使用功能较弱的抽象是正确的做法”。

例如:标准库未来 API 的zip是一个应用运算符,它允许您并行运行期货,如果您使用bar() zip foo()而不是for { f <- foo(); b <- bar() } yield (f, b) for { f <- foo(); b <- bar() } yield (f, b)你实际上可以加速你的程序(在很多情况下)。 对于其他类型,使用 applicative functor 的东西而不是 monadic bind 提供了其他类型的优化可能性。

Option的情况并非如此。 根据flatMap定义ap并非不合理。 使用应用性组合子仍是“做正确的事”,但flatMap就在那里,不需要额外的定义或依赖和for -comprehensions是如此简单和干净。 对于期货之类的东西,您看到的回报并不相同。

在他们的书 'Finding Success (and Failure) with Haskell' 中,Julie Moronuki 和 Chris Martin 有一个很好的例子来理解 Applicative 和 Monad 之间的区别 - 我发现它非常有用,因此我基于以下幻灯片:https:/ /www.slideshare.net/pjschwarz/applicative-functor-part-2

在其中,我将运行示例翻译成 Scala。

这是他们的程序开始的方式,在他们开始从任一个 Monad 到验证应用程序的过程之前:

在此处输入图片说明

检查一下,看看他们如何转换/改进代码(下载以获得最佳质量)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM