此文件頁面專屬於 Scala 3,且可能涵蓋 Scala 2 中沒有的新概念。除非另有說明,此頁面中的所有程式碼範例都假設您使用 Scala 3。
依賴函數類型描述函數類型,其中結果類型可能取決於函數的參數值。依賴類型和依賴函數類型的概念較為進階,通常只有在設計自己的函式庫或使用進階函式庫時才會遇到。
依賴方法類型
讓我們考慮以下異質資料庫範例,它可以儲存不同型別的值。金鑰包含有關對應值型別的資訊
trait Key { type Value }
trait DB {
def get(k: Key): Option[k.Value] // a dependent method
}
給定一個金鑰,方法 get
讓我們存取映射,並有可能傳回型別為 k.Value
的儲存值。我們可以將這個路徑依賴型別讀為:「根據參數 k
的具體型別,我們傳回一個匹配值」。
例如,我們可以有以下金鑰
object Name extends Key { type Value = String }
object Age extends Key { type Value = Int }
現在,對方法 get
的以下呼叫會進行型別檢查
val db: DB = ...
val res1: Option[String] = db.get(Name)
val res2: Option[Int] = db.get(Age)
呼叫方法 db.get(Name)
會傳回型別為 Option[String]
的值,而呼叫 db.get(Age)
會傳回型別為 Option[Int]
的值。傳回型別取決於傳遞給 get
的參數的具體型別,因此稱為依賴型別。
依賴函數類型
如上所見,Scala 2 已經支援依賴方法型別。但是,建立型別為 DB
的值相當麻煩
// a user of a DB
def user(db: DB): Unit =
db.get(Name) ... db.get(Age)
// creating an instance of the DB and passing it to `user`
user(new DB {
def get(k: Key): Option[k.Value] = ... // implementation of DB
})
我們需要手動建立 DB
的匿名內部類別,實作 get
方法。對於依賴建立許多不同 DB
執行個體的程式碼,這非常繁瑣。
特質 DB
僅有一個抽象方法 get
。如果我們可以使用 lambda 語法,那不是很好嗎?
user { k =>
... // implementation of DB
}
事實上,這現在在 Scala 3 中是可能的!我們可以將 DB
定義為依賴函數型別
type DB = (k: Key) => Option[k.Value]
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
// A dependent function type
給定這個 DB
定義,對 user
的上述呼叫會進行型別檢查,就像這樣。
您可以在 參考文件 中閱讀更多有關相依函數類型的內部結構。
案例研究:數值表達式
假設我們想要定義一個模組,用於抽象數字的內部表示。這可能很有用,例如,實作自動導數的函式庫。
我們從定義數字模組開始
trait Nums:
// the type of numbers is left abstract
type Num
// some operations on numbers
def lit(d: Double): Num
def add(l: Num, r: Num): Num
def mul(l: Num, r: Num): Num
我們省略
Nums
的具體實作,但作為練習,您可以透過指定type Num = Double
並相應地實作方法來實作Nums
。
使用我們數字抽象的程式現在具有下列類型
type Prog = (n: Nums) => n.Num => n.Num
val ex: Prog = nums => x => nums.add(nums.lit(0.8), x)
計算像 ex
這樣的程式的導數的函數類型為
def derivative(input: Prog): Double
有了相依函數類型的便利性,使用不同的程式呼叫此函數非常方便
derivative { nums => x => x }
derivative { nums => x => nums.add(nums.lit(0.8), x) }
// ...
回想一下,上述編碼中的相同程式會是
derivative(new Prog {
def apply(nums: Nums)(x: nums.Num): nums.Num = x
})
derivative(new Prog {
def apply(nums: Nums)(x: nums.Num): nums.Num = nums.add(nums.lit(0.8), x)
})
// ...
與內容函數結合
擴充方法、內容函數 和相依函數的結合為函式庫設計師提供了強大的工具。例如,我們可以如下精簡上述函式庫
trait NumsDSL extends Nums:
extension (x: Num)
def +(y: Num) = add(x, y)
def *(y: Num) = mul(x, y)
def const(d: Double)(using n: Nums): n.Num = n.lit(d)
type Prog = (n: NumsDSL) ?=> n.Num => n.Num
// ^^^
// prog is now a context function that implicitly
// assumes a NumsDSL in the calling context
def derivative(input: Prog): Double = ...
// notice how we do not need to mention Nums in the examples below?
derivative { x => const(1.0) + x }
derivative { x => x * x + const(2.0) }
// ...
此頁面的貢獻者
內容
- 簡介
- Scala 特色
- 為何選擇 Scala 3?
- Scala 體驗
- Hello, World!
- REPL
- 變數和資料類型
- 控制結構
- 網域建模
- 方法
- 一級函數
- 單例物件
- 集合
- 內容抽象
- 頂層定義
- 摘要
- 類型簡介
- 字串內插
- 控制結構
- 網域建模
- 工具
- 物件導向建模
- 函數式建模
- 方法
- 方法特色
- Scala 3 的主要方法
- 摘要
- 函數
- 匿名函數
- 函數變數
- Eta 擴充
- 高階函數
- 撰寫自己的 map 方法
- 建立傳回函數的方法
- 摘要
- 封裝和匯入
- Scala 集合
- 集合類型
- 集合方法
- 摘要
- 函數式程式設計
- 什麼是函數式程式設計?
- 不可變值
- 純函數
- 函數是值
- 函數式錯誤處理
- 摘要
- 類型和類型系統
- 推論類型
- 泛型
- 交集類型
- 聯集類型
- 代數資料類型
- 變異
- 不透明類型
- 結構類型
- 依賴函數類型
- 其他類型
- 內容抽象
- 擴充方法
- 內容參數
- 內容界限
- Given 匯入
- 類型類別
- 多重相等
- 隱式轉換
- 摘要
- 並行處理
- Scala 工具
- 使用 sbt 建置和測試 Scala 專案
- 工作表
- 與 Java 互動
- Java 開發人員的 Scala
- JavaScript 開發人員的 Scala
- Python 開發人員的 Scala
- 接下來要去哪裡