Scala 3 — 書籍

內容參數

語言

Scala 提供兩個重要的功能,用於內容抽象

  • 內容參數讓您指定參數,在呼叫位置,程式設計人員可以省略這些參數,而應由內容自動提供。
  • 給定實例(在 Scala 3 中)或隱式定義(在 Scala 2 中)是 Scala 編譯器可以用來填入遺失參數的術語。

內容參數

在設計系統時,通常需要將背景資訊(例如組態或設定)提供給系統的不同元件。達成此目的的一種常見方式是將組態傳遞為方法的額外參數。

在以下範例中,我們定義一個案例類別 Config 來建模一些網站組態,並在不同的方法中傳遞它。

case class Config(port: Int, baseUrl: String)

def renderWebsite(path: String, config: Config): String =
  "<html>" + renderWidget(List("cart"), config)  + "</html>"

def renderWidget(items: List[String], config: Config): String = ???

val config = Config(8080, "docs.scala-lang.org")
renderWebsite("/home", config)

讓我們假設組態不會在我們大部分的程式碼庫中變更。將 config 傳遞給每個方法呼叫(例如 renderWidget)會變得非常繁瑣,也會讓我們的程式更難閱讀,因為我們需要忽略 config 參數。

將參數標記為背景

我們可以將方法的一些參數標記為背景

def renderWebsite(path: String)(implicit config: Config): String =
    "<html>" + renderWidget(List("cart")) + "</html>"
    //                                  ^
    //                   no argument config required anymore

def renderWidget(items: List[String])(implicit config: Config): String = ???
def renderWebsite(path: String)(using config: Config): String =
    "<html>" + renderWidget(List("cart")) + "</html>"
    //                                  ^
    //                   no argument config required anymore

def renderWidget(items: List[String])(using config: Config): String = ???

透過在 Scala 3 中使用關鍵字 using 或在 Scala 2 中使用 implicit 來開始參數區段,我們告訴編譯器,它應該在呼叫位置自動尋找具有正確類型的參數。因此,Scala 編譯器會執行術語推論

在我們呼叫 renderWidget(List("cart")) 時,Scala 編譯器會看到範圍內有一個類型為 Config 的術語(config),並自動將它提供給 renderWidget。因此,程式等同於上述程式。

事實上,由於我們不再需要在 renderWebsite 的實作中參照 config,我們甚至可以在 Scala 3 的簽章中省略它的名稱

//        no need to come up with a parameter name
//                             vvvvvvvvvvvvv
def renderWebsite(path: String)(using Config): String =
    "<html>" + renderWidget(List("cart")) + "</html>"

在 Scala 2 中,隱式參數的名稱仍然是強制性的。

明確提供情境參數

我們已經了解如何對情境參數進行抽象化,並且 Scala 編譯器可以自動為我們提供參數。但是,我們如何指定在呼叫 renderWebsite 時要使用的組態?

我們明確提供參數值,就像它是一個常規參數一樣

renderWebsite("/home")(config)

就像我們使用 using 指定參數區段一樣,我們也可以使用 using 明確提供情境參數

renderWebsite("/home")(using config)

如果範圍內有多個不同的值有意義,並且我們希望確保將正確的值傳遞給函數,則明確提供情境參數會很有用。

對於所有其他情況,正如我們將在下一節中看到的那樣,還有一種將情境值帶入範圍的方法。

給定實例(Scala 2 中的隱式定義)

我們已經了解到,我們可以明確傳遞參數作為情境參數。但是,如果特定類型有一個單一的正規值,則有另一種首選方式可以讓 Scala 編譯器可以使用它:在 Scala 3 中將其標記為 given 或在 Scala 2 中將其標記為 implicit

implicit val config: Config = Config(8080, "docs.scala-lang.org")
//           ^^^^^^
// this is the value the Scala compiler will infer
// as argument to contextual parameters of type Config
val config = Config(8080, "docs.scala-lang.org")

// this is the type that we want to provide the
// canonical value for
//    vvvvvv
given Config = config
//             ^^^^^^
// this is the value the Scala compiler will infer
// as argument to contextual parameters of type Config

在上面的範例中,我們指定無論何時在當前範圍中省略類型為 Config 的情境參數,編譯器都應將 config 推斷為參數。

定義 Config 類型的正規值後,我們可以如下呼叫 renderWebsite

renderWebsite("/home")
//                   ^
//   again no argument

詳細說明 Scala 在何處尋找正規值,請參閱 常見問題

此頁面的貢獻者