Overview
這篇文章來一起討論下Scala中的ADT,也就是Algebraic Data Types,這個名字乍聽之下有一種不明覺厲的感覺,容易讓初學(xué)者望而卻步,那么什么是ADT?
1. 什么是ADT
Algebraic Data Types,從名字可以看出來,ADT就是一種數(shù)據(jù)類型,我們可以用它來結(jié)構(gòu)化數(shù)據(jù)。且我們也可以把ADT當作一種pattern來使用,因為他能作為一種通用方法解決一些常見場景。
1.1 Sum Type
舉個ADT的簡單例子
sealed trait Color
case object Red extends Color
case object Yellow extends Color
case object Green extends Color
這個ADT的例子,也可以叫做是 Sum Type,這里的類型 Color 可以由一個具體的 object 類型來擴展,所以 Color 可能的類型也就是這個 object 類型的和(sum),即
type Color = Red + Yellow + Green
也就是說,Color類型就是Red 或者Yellow 或者Green,而因為 Sum Type 枚舉了某種類型所有可能的類型,所以我們也可以叫它 枚舉類型(Enumerated types)
Tips
- 為什么用 “sealed trait”?
- “sealed” 關(guān)鍵字使我們定義的所有trait以及其擴展類都必須在同一個文件下;
- “sealed”使得編譯器會做更詳細的檢查,例如在pattern matching使會檢查是否對所有類型都進行了處理,如果沒有編譯時會報錯;
- 為什么用 “case object”?
- “object”而不是“class”:擴展類型不帶參數(shù),則用 “object”,否則應(yīng)該使用 “class”;
- “case object” 而不是 “object”:因為 case 封裝了很多有用的方法,例如
unapply,equals,toString等,所有在scala實現(xiàn)過程中,如果能用case 盡量不用 class;
1.2 Product Type
另一個ADT的例子
case class Location(x: Double, y: Double)
這個ADT的例子,也可以叫做是 Product Type,這里的類型 Location 是由 x的值和 y 的值共同決定的,也就是其乘積(product)的個數(shù)決定的,即:
type Location = Long * Long
這里也可以看出一點Scala和數(shù)學(xué)之間的關(guān)系,Product Type時和 與操作 (AND operation) 相關(guān)聯(lián),而 Sum Type和 或操作 (OR operation)相關(guān)聯(lián)。
1.3 Hybrid Type
再來一個ADT的例子
sealed trait Response
case class OK(v: String) extends Response
case class Error(error: String, description: String) extends Response
在這個ADT的例子中
-
Response是 Sum Type,因為其類型是OK或者Error; -
Error是 Product Type,因為其類型由error和description的乘積來決定其可能性; - 所以整體來說,這個ADT例子,也可以較多 Hybrid Type,也就是同時包括 Sum Type 和 Product Type;
Hybrid Type 也可以叫做 Sum of Product types
2. 為什么要使用ADT
網(wǎng)上或者書籍上其實沒有直接的解釋為什么要存在ADT,但這并不是一個在Scala中才出現(xiàn)的新概念,Scala最早借鑒了 Haskell 中的概念。
Scala中很多常用的類型,例如 Option, Either, IO, Reader, Writer 等都是ADT,而我們在寫Scala代碼的過程中為了消除Side Effect也會自定義ADT,其存在對FP有著重要的意義。其推導(dǎo)過程,可以參考這篇博客