В алгебре, как и в повседневном формировании понятий, абстракции формируются путем группировки вещей по некоторым существенным характеристикам и исключения их специфических других характеристик.Абстракция объединяется под одним символом или словом, обозначающим сходство.Мы говорим, что мы обобщаем различия, но это действительно означает, что мы интегрируем по сходствам.
Например, рассмотрим программу, которая принимает суммучисла 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
является моноидом или может быть преобразован в один.Например:
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)
У нас есть абстрагированных над моноидами и складными.