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