簡體   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