了解Scala应用函数

问题描述 投票:1回答:2

假设我需要编写一些函数来调用一些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,我从配置和调用函数hostportapi1中读取了api2api3,它们添加了所需的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)

虽然最好使用函数Path => Url(如果我们用builder pattern编写,则使用Java更好)来[[隐藏 hostport以及构造Url的其他细节]。

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

curring

很容易实现这样的功能f: Path => Urlval 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是应用函子,如果它是函子并且具有两个附加操作:
    [ap在给定M[B]M[A => B]的情况下获得M[A]
  • [pureM[A => B]获得A => B(对于SomeOption)]
  • 这些操作应遵循两个简单的法律,但这是另一回事。

    ...

    这有意义吗?

  • scala functional-programming functor applicative
    2个回答
    5
    投票
    对我来说,这听起来很合理,尽管我不确定这里是否有很多问题,这是它本身的问题。

    我将其作为答案而不是评论,因为有一件事值得注意。对于许多类型,除了“使用功能较弱的抽象是正确的做法”之外,还有一个避免单子绑定并坚持[C0​​]的理由。

    例如:标准库Future API的ap是可应用的运算符,它允许您并行运行期货,如果使用zip而不是bar() zip foo(),则实际上可以加快程序运行速度(在许多情况下) 。对于其他类型,使用可应用的函子填充而不是单子绑定为优化提供了其他可能性。

    for { f <- foo(); b <- bar() } yield (f, b)并非如此。根据Option定义ap并非没有道理。使用应用组合器仍然是“正确的事情”,但是flatMap就在那里,不需要额外的定义或依赖项,并且flatMap理解是如此简单和简洁。您看到的诸如期货之类的收益不一样。


    1
    投票
    [在他们的书《用Haskell寻找成功(和失败)》中,朱莉·莫朗基(Julie Moronuki)和克里斯·马丁(Chris Martin)有一个很好的例子,可以理解Applicative和Monad之间的区别-我发现它非常有用,因此我将以下幻灯片作为基础: C0]。
    © www.soinside.com 2019 - 2024. All rights reserved.