问题描述
经常在 Scala 文献中,我遇到抽象结束"这个词,但我不明白其意图.例如,Martin Odersky 写道
Often in the Scala literature, I encounter the phrase "abstract over", but I don't understand the intent. For example, Martin Odersky writes
您可以将方法(或函数")作为参数传递,或者您可以抽象它们.您可以将类型指定为参数,也可以抽象它们.
再举一个例子,在弃用观察者模式"论文中,
As another example, in the "Deprecating the Observer Pattern" paper,
我们的事件流是一流值的结果是我们可以抽象它们.
我读过一阶泛型抽象于类型",而单子抽象于类型构造函数".我们还在蛋糕图案纸.引用许多这样的例子之一:
I have read that first order generics "abstract over types", while monads "abstract over type constructors". And we also see phrases like this in the Cake Pattern paper. To quote one of many such examples:
抽象类型成员提供了灵活的方式来抽象具体类型的组件.
甚至相关的堆栈溢出问题也使用这个术语.不能对参数化类型进行存在抽象......"
Even relevant stack overflow questions use this terminology. "can't existentially abstract over parameterized type..."
那么……抽象"到底是什么意思?
So... what does "abstract over" actually mean?
推荐答案
在代数中,就像在日常概念形成中一样,抽象是通过按一些基本特征对事物进行分组并省略其特定的其他特征而形成的.抽象在表示相似性的单个符号或单词下统一.我们说我们抽象了差异,但这实际上意味着我们通过相似性整合.
In algebra, as in everyday concept formation, abstractions are formed by grouping things by some essential characteristics and omitting their specific other characteristics. The abstraction is unified under a single symbol or word denoting the similarities. We say that we abstract over the differences, but this really means we're integrating by the similarities.
例如,考虑一个程序,它对数字 1
、2
和 3
求和:
For example, consider a program that takes the sum of the numbers 1
, 2
, and 3
:
val sumOfOneTwoThree = 1 + 2 + 3
这个程序不是很有趣,因为它不是很抽象.我们可以抽象我们正在求和的数字,通过将所有数字列表整合到一个符号 ns
下:
This program is not very interesting, since it's not very abstract. We can abstract over the numbers we're summing, by integrating all lists of numbers under a single symbol ns
:
def sumOf(ns: List[Int]) = ns.foldLeft(0)(_ + _)
而且我们也不特别关心它是一个列表.List 是一个特定的类型构造函数(接受一个类型并返回一个类型),但是我们可以通过指定我们想要的基本特征(它可以折叠)来抽象类型构造函数:
And we don't particularly care that it's a List either. List is a specific type constructor (takes a type and returns a type), but we can abstract over the type constructor by specifying which essential characteristic we want (that it can be folded):
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)
我们可以为 List
和任何其他我们可以折叠的东西提供隐式的 Foldable
实例.
And we can have implicit Foldable
instances for List
and any other thing we can fold.
implicit val listFoldable = new Foldable[List] {
def foldl[A, B](as: List[A], z: B, f: (B, A) => B) = as.foldLeft(z)(f)
}
implicit val setFoldable = new Foldable[Set] {
def foldl[A, B](as: Set[A], z: B, f: (B, A) => B) = as.foldLeft(z)(f)
}
val sumOfOneTwoThree = sumOf(List(1,2,3))
更重要的是,我们可以抽象操作数和操作数的类型:
What's more, we can abstract over both the operation and the type of the operands:
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
是一个幺半群或可以映射成一个.例如:
Now we have something quite general. The method mapReduce
will fold any F[A]
given that we can prove that F
is foldable and that A
is a monoid or can be mapped into one. For example:
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(Set(4,5,6), Product)
我们已经抽象了幺半群和可折叠体.
We have abstracted over monoids and foldables.
这篇关于什么是“抽象过度"?意思?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!