In der Algebra wie in der alltäglichen Konzeptbildung werden Abstraktionen gebildet, indem Dinge nach bestimmten wesentlichen Merkmalen gruppiert und ihre spezifischen anderen Merkmale weggelassen werden. Die Abstraktion wird unter einem einzigen Symbol oder Wort vereinheitlicht, das die Ähnlichkeiten bezeichnet. Wir sagen , dass wir abstrakt über die Unterschiede, aber das wirklich bedeutet , dass wir die Integration von den Ähnlichkeiten.
Betrachten wir zum Beispiel ein Programm, das die Summe der Zahlen nimmt 1
, 2
und 3
:
val sumOfOneTwoThree = 1 + 2 + 3
Dieses Programm ist nicht sehr interessant, da es nicht sehr abstrakt ist. Wir können über die Zahlen, die wir summieren, abstrahieren , indem wir alle Listen von Zahlen unter einem einzigen Symbol integrieren ns
:
def sumOf(ns: List[Int]) = ns.foldLeft(0)(_ + _)
Und es ist uns auch nicht besonders wichtig, dass es sich um eine Liste handelt. List ist ein bestimmter Typkonstruktor (nimmt einen Typ und gibt einen Typ zurück), aber wir können über den Typkonstruktor abstrahieren, indem wir angeben, welches wesentliche Merkmal wir wollen (dass es gefaltet werden kann):
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)
Und wir können implizite Foldable
Instanzen für List
und alles andere haben, was wir folden können.
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))
Darüber hinaus können wir sowohl die Operation als auch den Typ der Operanden abstrahieren :
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)
Jetzt haben wir etwas ganz Allgemeines. Die Methode mapReduce
wird alles falten, F[A]
vorausgesetzt, wir können beweisen, dass F
es faltbar ist und dass A
es sich um ein Monoid handelt oder dass es in eines abgebildet werden kann. Beispielsweise:
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)
Wir haben über Monoide und Faltblätter abstrahiert .