Overview
ADT(Algebraic Data Type, 代数数据类型),是函数式编程(FP)构建组合类型的方式。
编程语言会内置一些基本类型(Primitive Data Types, Byte, Short, Int, Long, Float, Double, Boolean, Char),但是基本类型只能表示非常简单的值,无法对复杂的现实世界建模,因此编程语言还会提供将基本类型
组合成复合类型
。
不同编程语言提供了不同的构建组合类型的机制,例如,常见于C++,java的是结构体struct,类class,而在scala里面就是ADT。
代数模型有三类product、sum和Hybrid,常见是前两类,
- product,是一种组合,即A和B,如Tuple(), trait Function2()
- scala可以用
case class
构建product类型 - 两种类型是
同时
存在的 - 如,
case class Pair ( x: Int, y: Int )
- scala可以用
- sum,是一种选择,即A或者B,如Option(只能是Some or None)
- scala通过
sealed trait
+case object/class
构建sum类型 - sealed
- 其中sealed用于行程闭环,类型的定义已经限定了所有可能性,不会出现其他可能性了
- sealed自身及其子类只能在同一.scala文件下被继承
- case object/ case class
- object可以提供单例
- case提供了unapply()自动解构函数,便于
match
- 自动重写了equals,hashCode,toString
- 自动扩展了Serializable
- 如,
// 方向例子(单例) // 这里当然也可以用case class来实现非单例的形式,即hybrid的模式 sealed trait Direction case object North extends Direction case object South extends Direction case object East extends Direction case object West extends Direction // 布尔例子(单例) sealed trait Bool case object True extends Bool case object False extends Bool
- scala通过
- hybrid,是product和sum的组合,即
Sum of Products
- 如,
// 非单例模式,这里用了case class sealed trait Shape // Sum(限定只能是圆形`或`矩形两类) final case class Circle(radius: Double) extends Shape // Product final case class Rectangle(width: Double, height: Double) extends Shape // Product
- 如,
sealed自身及其子类只能在同一.scala文件下被继承
用途
ADT最大的价值是用于模式匹配
,即解构
一个对象。
解构对象之后,就可以避免使用很多if-else来组织分支,而是直接使用case来解出。
// 代数数据类型
// 主要希望利用一些代数的性质
// 常见的代数类型是product和sum
// ADT最大的价值是用于`模式匹配`,即解构一个对象
object ADT {
// example 0
sealed trait Tree // sealed闭环
case class Node(left: Tree, right: Tree) extends Tree
case class Leaf[A](value: A) extends Tree
// example 1
sealed abstract class Coupon {
val id: Long
val discount: Double
val status: Boolean
}
case class CashCoupon(id: Long, discount: Double, status: Boolean = false) extends Coupon
case class DiscountCoupon(id: Long, discount: Double, status: Boolean) extends Coupon
case class GiftCoupon(id: Long, discount: Double, status: Boolean) extends Coupon
case class GroupCoupon(id: Long, discount: Double, status: Boolean) extends Coupon
sealed trait CouponStatus {
val base: CouponStatusBase //每种状态共用的一些信息
}
case class CouponStatusBase(coupon: Coupon)
case class StatusNotFetched(base: CouponStatusBase) extends CouponStatus //未领取
case class StatusFetched(base: CouponStatusBase, user: Int) extends CouponStatus //已领取但未使用
case class StatusUsed(base: CouponStatusBase, user: Int) extends CouponStatus //已使用
case class StatusExpired(base: CouponStatusBase, user: Int) extends CouponStatus //过期优惠券
def pay(status: CouponStatus): Unit = status match {
case StatusNotFetched(base) => println(s"base=$base")
case StatusFetched(base, user) => println(s"base=$base, user=$user")
case StatusUsed(base, user) => println(s"base=$base, user=$user")
case StatusExpired(base, user) => println(s"base=$base, user=$user")
}
// example 2
sealed trait PaymentMethod
case class Cash() extends PaymentMethod
case class Cheque(chequeNumber: String) extends PaymentMethod
sealed abstract class CardType // trait或者abstract class都行
case class Visa() extends CardType
case object MasterCard extends CardType // object或者case class都行
case class Card(cardType: CardType, cardNumber: String) extends PaymentMethod
def payMe(method: PaymentMethod): Unit = method match {
case Cash() ⇒ println("Pay with cash")
case Cheque(checkNum) ⇒ println(s"Pay with cheque: $checkNum")
case Card(Visa(), cardNum) => println(s"Pay with a Visa card: $cardNum")
case Card(MasterCard, cardNum) => println(s"Pay with a MasterCard: $cardNum")
}
// example 3
sealed trait Notification
case class Email(to: String) extends Notification
case object Slack extends Notification
def sendNotification(e: Option[Notification]): Unit = e match {
case Some(Email(to)) => println(1) // send email
case Some(Slack) => println(2) // send message to Slack
case None => println(3)
}
def main(args: Array[String]): Unit = {
// example 1
val c1 = CashCoupon(1, 0.9, status = true)
val c2 = DiscountCoupon(2, 0.8, status = true)
val c3 = GiftCoupon(3, 0.7, status = true)
val c4 = GroupCoupon(4, 0.6, status = true)
val s1 = StatusNotFetched(CouponStatusBase(c1))
val s2 = StatusFetched(CouponStatusBase(c2), 97)
val s3 = StatusUsed(CouponStatusBase(c3), 98)
val s4 = StatusExpired(CouponStatusBase(c4), 99)
pay(s1)
pay(s2)
pay(s3)
pay(s4)
println()
// example 2
val e1 = Cash()
val e2 = Cheque("123456")
val e3 = Card(Visa(), "54321")
val e4 = Card(MasterCard, "98765")
payMe(e1)
payMe(e2)
payMe(e3)
payMe(e4)
// example 3
val r1 = Email("chenfh5@qq.com")
val r2 = Slack
sendNotification(Option(r1))
sendNotification(Option(r2))
sendNotification(Option(null))
}
}
Summary
ADT在scala的含义,
- 将基本类型组合起来,构成一个复合类型
- 使用一些sealed,case class,case特性将简易化解构过程
Reference
PREVIOUS消息队列关注点
NEXTiTerm2个人配置