Dalam aljabar, seperti dalam pembentukan konsep sehari-hari, abstraksi dibentuk dengan mengelompokkan hal-hal berdasarkan beberapa karakteristik esensial dan menghilangkan karakteristik spesifik lainnya. Abstraksi disatukan di bawah satu simbol atau kata yang menunjukkan kesamaan. Kami mengatakan bahwa kami mengabstraksikan perbedaan, tetapi ini benar-benar berarti kami terintegrasi oleh kesamaan.
Sebagai contoh, mempertimbangkan program yang mengambil jumlah dari angka 1
, 2
dan 3
:
val sumOfOneTwoThree = 1 + 2 + 3
Program ini tidak terlalu menarik, karena tidak terlalu abstrak. Kita dapat mengabstraksi bilangan yang kita jumlahkan, dengan mengintegrasikan semua daftar bilangan di bawah satu simbol ns
:
def sumOf(ns: List[Int]) = ns.foldLeft(0)(_ + _)
Dan kami juga tidak terlalu peduli bahwa itu adalah Daftar. List adalah konstruktor tipe tertentu (mengambil tipe dan mengembalikan tipe), tetapi kita bisa mengabstraksi konstruktor tipe dengan menentukan karakteristik penting mana yang kita inginkan (yang dapat dilipat):
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)
Dan kita dapat memiliki Foldable
contoh implisit untuk List
dan hal lain yang dapat kita lipat.
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))
Terlebih lagi, kita dapat mengabstraksi operasi dan jenis operan:
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)
Sekarang kami memiliki sesuatu yang cukup umum. Metode ini mapReduce
akan melipat apa pun F[A]
yang kita dapat buktikan bahwa F
dapat dilipat dan itu A
adalah monoid atau dapat dipetakan menjadi satu. Sebagai contoh:
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)
Kami telah mengabstraksi monoid dan lipat.