ในพีชคณิตเช่นเดียวกับการสร้างแนวคิดในชีวิตประจำวัน Abstractions เกิดจากการจัดกลุ่มสิ่งต่าง ๆ ตามลักษณะสำคัญบางประการและละเว้นลักษณะเฉพาะอื่น ๆ สิ่งที่เป็นนามธรรมรวมเป็นหนึ่งเดียวภายใต้สัญลักษณ์หรือคำเดียวที่แสดงถึงความคล้ายคลึงกัน เราบอกว่าเราเป็นนามธรรมเหนือความแตกต่าง แต่นี่หมายความว่าเรากำลังรวมเข้าด้วยกันโดยความคล้ายคลึงกัน
ตัวอย่างเช่นพิจารณาโปรแกรมที่ใช้ผลรวมของตัวเลขที่1
, 2
และ3
:
val sumOfOneTwoThree = 1 + 2 + 3
โปรแกรมนี้ไม่น่าสนใจมากนักเนื่องจากมันไม่ได้เป็นนามธรรมมากนัก เราสามารถสรุปตัวเลขที่เรากำลังสรุปได้โดยการรวมรายการตัวเลขทั้งหมดไว้ในสัญลักษณ์เดียวns
:
def sumOf(ns: List[Int]) = ns.foldLeft(0)(_ + _)
และเราไม่สนใจเป็นพิเศษว่าเป็น List ด้วย รายการเป็นตัวสร้างชนิดเฉพาะ (รับประเภทและส่งคืนประเภท) แต่เราสามารถสรุปเหนือตัวสร้างประเภทได้โดยระบุลักษณะสำคัญที่เราต้องการ (ซึ่งสามารถพับเก็บได้):
trait Foldable[F[_]] {
def foldl[A, B](as: F[A], z: B, f: (B, A) => B): B
}
def sumOf[F[_]](ns: F[Int])(implicit ff: Foldable[F]) =
ff.foldl(ns, 0, (x: Int, y: Int) => x + y)
และเราสามารถมีFoldable
อินสแตนซ์โดยนัยสำหรับList
และสิ่งอื่น ๆ ที่เราพับได้
implicit val listFoldable = new Foldable[List] {
def foldl[A, B](as: List[A], z: B, f: (B, A) => B) = as.foldLeft(z)(f)
}
val sumOfOneTwoThree = sumOf(List(1,2,3))
ยิ่งไปกว่านั้นเราสามารถสรุปได้ทั้งการดำเนินการและประเภทของตัวถูกดำเนินการ:
trait Monoid[M] {
def zero: M
def add(m1: M, m2: M): M
}
trait Foldable[F[_]] {
def foldl[A, B](as: F[A], z: B, f: (B, A) => B): B
def foldMap[A, B](as: F[A], f: A => B)(implicit m: Monoid[B]): B =
foldl(as, m.zero, (b: B, a: A) => m.add(b, f(a)))
}
def mapReduce[F[_], A, B](as: F[A], f: A => B)
(implicit ff: Foldable[F], m: Monoid[B]) =
ff.foldMap(as, f)
ตอนนี้เรามีบางสิ่งที่ค่อนข้างทั่วไป วิธีmapReduce
นี้จะพับอะไรF[A]
ก็ได้ที่เราสามารถพิสูจน์ได้ว่าF
พับได้และนั่นA
คือ monoid หรือสามารถแมปเป็นชิ้นเดียวได้ ตัวอย่างเช่น:
case class Sum(value: Int)
case class Product(value: Int)
implicit val sumMonoid = new Monoid[Sum] {
def zero = Sum(0)
def add(a: Sum, b: Sum) = Sum(a.value + b.value)
}
implicit val productMonoid = new Monoid[Product] {
def zero = Product(1)
def add(a: Product, b: Product) = Product(a.value * b.value)
}
val sumOf123 = mapReduce(List(1,2,3), Sum)
val productOf456 = mapReduce(List(4,5,6), Product)
เราได้แยกแยะเรื่องโมโนและพับเก็บได้