“抽象过度”是什么意思?意思是?

发布于 2024-10-13 07:26:32 字数 891 浏览 2 评论 0原文

在 Scala 文献中,我经常遇到“抽象过度”这个短语,但我不明白其意图。 例如,Martin Odersky 写道

您可以将方法(或“函数”)作为参数传递,也可以对它们进行抽象。您可以将类型指定为参数,也可以对它们进行抽象。

另一个例子,在“弃用观察者模式”论文中,

我们的事件流是一流的值,因此我们可以对它们进行抽象。

我读过一阶泛型“抽象于类型”,而 monad 则“抽象于类型构造函数”。我们还在 Cake Pattern 论文。引用许多这样的例子之一:

抽象类型成员提供了灵活的方式来抽象组件的具体类型。

甚至相关的堆栈溢出问题也使用这个术语。 “不能对参数化类型进行存在抽象...”

所以...“抽象结束”到底意味着什么?

Often in the Scala literature, I encounter the phrase "abstract over", but I don't understand the intent. For example, Martin Odersky writes

You can pass methods (or "functions") as parameters, or you can abstract over them. You can specify types as parameters, or you can abstract over them.

As another example, in the "Deprecating the Observer Pattern" paper,

A consequence from our event streams being first-class values is that we can abstract over them.

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:

Abstract type members provide flexible way to abstract over concrete types of components.

Even relevant stack overflow questions use this terminology. "can't existentially abstract over parameterized type..."

So... what does "abstract over" actually mean?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

鲜肉鲜肉永远不皱 2024-10-20 07:26:32

在代数中,就像在日常概念形成中一样,抽象是通过根据某些基本特征对事物进行分组并忽略其特定的其他特征而形成的。抽象统一在表示相似性的单个符号或单词下。我们说我们对差异进行了抽象,但这实际上意味着我们通过相似性进行了整合。

例如,考虑一个计算数字 123 之和的程序:

val sumOfOneTwoThree = 1 + 2 + 3

这个程序不是很有趣,因为它是不是很抽象。我们可以通过将所有数字列表整合到一个符号 ns 下来抽象我们正在求和的数字:

def sumOf(ns: List[Int]) = ns.foldLeft(0)(_ + _)

而且我们也并不特别关心它是否是一个列表。 可以折叠)来抽象类型构造函数:

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 是一个特定的类型构造函数(接受一个类型并返回一个类型),但是我们可以通过指定我们想要的基本特征(它 List 和任何其他我们可以折叠的东西的隐式 Foldable 实例。

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))

更重要的是,我们可以对操作和操作数的类型进行抽象:

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)

现在我们有了一些非常通用的东西。如果我们可以证明 F 是可折叠的并且 A ,则 mapReduce 方法将折叠任何 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(Set(4,5,6), Product)

我们对幺半群和可折叠对象进行了抽象。

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.

For example, consider a program that takes the sum of the numbers 1, 2, and 3:

val sumOfOneTwoThree = 1 + 2 + 3

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)(_ + _)

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)

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)

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.

请帮我爱他 2024-10-20 07:26:32

首先近似地说,能够“抽象”某些东西意味着您可以为其设置参数,或者以其他方式“匿名”使用它,而不是直接使用该东西。

Scala 允许您对类型进行抽象,方法是允许类、方法和值具有类型参数,并且允许值具有抽象(或匿名)类型。

Scala 允许您通过允许方法具有函数参数来抽象操作。

Scala 允许您通过结构性地定义类型来抽象功能。

Scala 允许您通过允许高阶类型参数来抽象类型参数。

Scala 允许您创建提取器,从而抽象数据访问模式。

Scala 允许您通过允许隐式转换作为参数来抽象“可以用作其他东西的东西”。 Haskell 对类型类也做了类似的事情。

Scala(还)不允许您对类进行抽象。您不能将类传递给某些对象,然后使用该类来创建新对象。其他语言确实允许对类进行抽象。

(“Monads 对类型构造函数的抽象”只有在非常严格的情况下才是正确的。在你有“啊哈!我理解 monads!!”的时刻之前不要沉迷于此。)

对计算的某些方面进行抽象的能力基本上是允许代码重用,并允许创建功能库。与更主流的语言相比,Scala 允许抽象更多种类的事物,并且 Scala 中的库也相应地更强大。

To a first approximation, being able to "abstract over" something means that instead of using that something directly, you can make a parameter of it, or otherwise use it "anonymously".

Scala allows you to abstract over types, by allowing classes, methods, and values to have type parameters, and values to have abstract (or anonymous) types.

Scala allows you to abstract over actions, by allowing methods to have function parameters.

Scala allows you to abstract over features, by allowing types to be defined structurally.

Scala allows you to abstract over type parameters, by allowing higher-order type parameters.

Scala allows you to abstract over data access patterns, by allowing you to create extractors.

Scala allows you to abstract over "things that can be used as something else", by allowing implicit conversions as parameters. Haskell does similarly with type classes.

Scala doesn't (yet) allow you to abstract over classes. You can't pass a class to something, and then use that class to create new objects. Other languages do allow abstraction over classes.

("Monads abstract over type constructors" is only true in a very restrictive way. Don't get hung up on it until you have your "Aha! I understand monads!!" moment.)

The ability to abstract over some aspect of computation is basically what allows code reuse, and enables the creation of libraries of functionality. Scala allows many more sorts of things to be abstracted over than more mainstream languages, and libraries in Scala can be correspondingly more powerful.

街角卖回忆 2024-10-20 07:26:32

抽象是一种概括。

http://en.wikipedia.org/wiki/Abstraction

不仅在 Scala 中,而且在许多语言中需要有这样的机制来降低复杂性(或者至少创建一个层次结构,将信息划分为更容易理解的部分)。

类是简单数据类型的抽象。它有点像基本类型,但实际上概括了它们。因此,类不仅仅是一种简单的数据类型,而且有许多共同点。

当他说“抽象”时,他指的是概括的过程。因此,如果您将方法抽象为参数,那么您就是在概括执行此操作的过程。例如,您可以创建某种类型的通用方法来处理它,而不是将方法传递给函数(例如根本不传递方法,而是构建一个特殊的系统来处理它)。

在这种情况下,他特指抽象问题并创建类似 oop 的问题解决方案的过程。 C 几乎没有抽象能力(你可以做到,但它很快就会变得混乱,而且该语言不直接支持它)。如果你用 C++ 编写它,你可以使用 oop 概念来降低问题的复杂性(嗯,它是相同的复杂性,但概念化通常更容易(至少一旦你学会用抽象来思考))。

例如,如果我需要一个类似于 int 的特殊数据类型,但假设受到限制,我可以通过创建一个可以像 int 一样使用但具有我需要的属性的新类型来抽象它。我用来做这样的事情的过程被称为“抽象”。

An abstraction is a sort of generalization.

http://en.wikipedia.org/wiki/Abstraction

Not only in Scala but many languages there is a need to have such mechanisms to reduce complexity(or at least create a hierarchy that partitions information into easier to understand pieces).

A class is an abstraction over a simple data type. It is sort of like a basic type but actually generalizes them. So a class is more than a simple data type but has many things in common with it.

When he says "abstracting over" he means the process by which you generalize. So if you are abstracting over methods as parameters you are generalizing the process of doing that. e.g., instead of passing methods to functions you might create some type of generalized way to handle it(such as not passing methods at all but building up a special system to deal with it).

In this case he specifically means the process of abstracting a problem and creating a oop like solution to the problem. C has very little ability to abstract(you can do it but it gets messy real quick and the language doesn't directly support it). If you wrote it in C++ you could use oop concepts to reduce the complexity of the problem(well, it's the same complexity but the conceptualization is generally easier(at least once you learn to think in terms of abstractions)).

e.g., If I needed a special data type that was like an int but, lets say restricted I could abstract over it by creating a new type that could be used like an int but had those properties I needed. The process I would use to do such a thing would be called an "abstracting".

时光无声 2024-10-20 07:26:32

这是我狭隘的表演和讲述的解释。它是不言自明的并且在 REPL 中运行。

class Parameterized[T] { // type as a parameter
  def call(func: (Int) => Int) = func(1)  // function as a parameter
  def use(l: Long) { println(l) } // value as a parameter
}

val p = new Parameterized[String] // pass type String as a parameter
p.call((i:Int) => i + 1) // pass function increment as a parameter
p.use(1L) // pass value 1L as a parameter


abstract class Abstracted { 
  type T // abstract over a type
  def call(i: Int): Int // abstract over a function
  val l: Long // abstract over value
  def use() { println(l) }
}

class Concrete extends Abstracted { 
  type T = String // specialize type as String
  def call(i:Int): Int = i + 1 // specialize function as increment function
  val l = 1L // specialize value as 1L
}

val a: Abstracted = new Concrete
a.call(1)
a.use()

Here is my narrow show and tell interpretation. It's self-explanatory and runs in the REPL.

class Parameterized[T] { // type as a parameter
  def call(func: (Int) => Int) = func(1)  // function as a parameter
  def use(l: Long) { println(l) } // value as a parameter
}

val p = new Parameterized[String] // pass type String as a parameter
p.call((i:Int) => i + 1) // pass function increment as a parameter
p.use(1L) // pass value 1L as a parameter


abstract class Abstracted { 
  type T // abstract over a type
  def call(i: Int): Int // abstract over a function
  val l: Long // abstract over value
  def use() { println(l) }
}

class Concrete extends Abstracted { 
  type T = String // specialize type as String
  def call(i:Int): Int = i + 1 // specialize function as increment function
  val l = 1L // specialize value as 1L
}

val a: Abstracted = new Concrete
a.call(1)
a.use()
忆悲凉 2024-10-20 07:26:32

其他答案已经很好地说明了存在哪些抽象。让我们一一回顾一下引文,并提供一个例子:

您可以传递方法(或“函数”)
作为参数,或者你可以抽象
在他们之上。您可以将类型指定为
参数,或者你可以抽象
他们。

将函数作为参数传递:List(1,-2,3).map(math.abs(x)) 显然,abs 在这里作为参数传递。 map 本身抽象了一个函数,该函数对每个列表元素执行特定的特殊操作。 val list = List[String]() 指定类型参数(String)。您可以编写一个使用抽象类型成员的集合类型:val buffer = Buffer{ type Elem=String }。一个区别是您必须编写 def f(lis:List[String])...def f(buffer:Buffer)...,因此该元素类型在第二种方法中是“隐藏”的。

我们事件流的结果
成为一流的价值观是我们
可以对它们进行抽象。

在 Swing 中,事件只是突然“发生”,您必须此时此地处理它。事件流允许您以更具声明性的方式完成所有管道和接线。例如,当您想要更改 Swing 中的负责侦听器时,您必须取消注册旧侦听器并注册新侦听器,并了解所有血淋淋的细节(例如线程问题)。对于事件流,事件的变成了可以简单传递的东西,使其与字节或字符流没有太大区别,因此是一个更“抽象”的概念。

抽象类型成员提供灵活的
抽象具体类型的方法
组件。

上面的 Buffer 类已经是一个例子。

The other answers give already a good idea of what kinds of abstractions exist. Lets go over the quotes one by one, and provide an example:

You can pass methods (or "functions")
as parameters, or you can abstract
over them. You can specify types as
parameters, or you can abstract over
them.

Pass function as a parameter: List(1,-2,3).map(math.abs(x)) Clearly abs is passed as parameter here. map itself abstracts over a function that does a certain specialiced thing with each list element. val list = List[String]() specifies a type paramter (String). You could write a collection type which uses abstract type members instead: val buffer = Buffer{ type Elem=String }. One difference is that you have to write def f(lis:List[String])... but def f(buffer:Buffer)..., so the element type is kind of "hidden" in the second method.

A consequence from our event streams
being first-class values is that we
can abstract over them.

In Swing an event just "happens" out of the blue, and you have to deal with it here and now. Event streams allow you to do all the plumbing an wiring in a more declarative way. E.g. when you want to change the responsible listener in Swing, you have to unregister the old and to register the new one, and to know all the gory details (e.g. threading issues). With event streams, the source of the events becomes a thing you can simply pass around, making it not very different from a byte or char stream, hence a more "abstract" concept.

Abstract type members provide flexible
way to abstract over concrete types of
components.

The Buffer class above is already an example for this.

铁轨上的流浪者 2024-10-20 07:26:32

上面的答案提供了很好的解释,但用一句话概括它,我会说:

对某事进行抽象在不相关的地方忽略它完全相同。

Answers above provide an excellent explanation, but to summarize it in a single sentence, I would say:

Abstracting over something is the very same as neglecting it where irrelevant.

梦中的蝴蝶 2024-10-20 07:26:32

“过度”抽象意味着形成一种抽象,可以统一应用于所考虑的某些事物集中的每个元素。你结束的事情就是布景。这是非正式的数学术语。

Abstracting "over" means forming an abstraction that can be uniformly applied to every element in some set of things under consideration. The thing you're over is the set. It's informal math jargon.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文