What does “abstraction” mean?

Often in Scala literature, I come across the phrase “abstraction,” but I don't understand the intent. For example, writes Martin Odersky

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

As another example, in the "Obsolete Observer Pattern" ,

The consequence of our streams of events, which are first-class values, is that we can abstract over them.

I read that first-order generators are "abstract over types", and monads are "abstract over type constructors". And we also see such phrases in the Cake Pattern document. To quote one of many such examples:

Elements of the abstract type provide a flexible way of abstracting specific types of components.

Even relevant questions use this terminology. "cannot be existentially abstract by parameterized type ..."

So ... what does "abstract representation" mean?

+91
scala abstraction
Jan 22 '11 at 1:19
source share
5 answers

In algebra, as in the formation of the everyday concept, abstractions are formed by grouping units according to some essential characteristics and omitting their specific other characteristics. Abstraction is combined under one symbol or word denoting their similarity. We say that we abstract from differences, but that does mean that we integrate by similarity.

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 is not very abstract. Thus, we can abstract by specific numbers, combining all lists of numbers under one ns character:

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

And we don’t particularly care that this is a list. List is a specific type constructor (accepts a type and returns a type), but we can abstract from the type constructor by indicating what essential characteristic we want (so that it can be collapsed):

 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 that we can discard.

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

What else, we can abstract both by operation and by type of 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 in common. The mapReduce method will discard any F[A] , given that we can prove that F is folding and that A is a monoid or can be mapped to 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(List(4,5,6), Product) 

We abstracted over monoids and folds.

+117
Jan 22 2018-11-11T00:
source share

In a first approximation, the ability to “abstract over” means that instead of using something directly, you can make it a parameter or use it “anonymously”.

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

Scala allows you to ignore actions by letting methods have function parameters.

Scala allows you to abstract from functions, allowing structures to be defined structurally.

Scala allows you to abstract from type parameters by setting type parameters of a higher order.

Scala lets you ignore data access patterns by allowing you to create extractors.

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

Scala (for now) does not allow abstracting from classes. You cannot pass a class to something, and then use this class to create new objects. Other languages ​​allow you to abstract by class.

("Monads abstract over type constructors" applies only in a very restrictive sense. Do not dwell on it until your "Aha! I understand monads!" Appears).

The ability to ignore some aspects of computing is basically something that allows you to reuse code and allows you to create libraries of functionality. Scala allows you to abstract more than other types than other main languages, and libraries in Scala can be correspondingly more powerful.

+10
Jan 22 2018-11-11T00:
source share

Abstraction is a kind of generalization.

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

Not only in Scala, but in many languages, it is necessary to have such mechanisms to reduce complexity (or at least create a hierarchy that divides information into more understandable parts).

A class is an abstraction of a simple data type. This is similar to the base type, but actually generalizes them. Thus, a class is more than a simple data type, but has much in common with it.

When he says "abstraction," he means the process by which you generalize. Therefore, if you abstract over methods as parameters, you generalize the process of this. for example, instead of passing methods to functions, you can create some type of generalized method of processing it (for example, not passing methods at all, but creating a special system to deal with it).

In this case, it specifically means the process of abstracting the problem and creating such a solution to the problem. C has very few possibilities to abstract (you can do this, but it becomes messy and the language does not support it directly). If you wrote it in C ++, you could use oop concepts to reduce the complexity of the problem (well, the same complexity, but conceptualization is generally simpler (at least as soon as you learn to think in terms of abstractions)).

For example, if I need a special data type that looks like an int, but let's say I could abstract from it by creating a new type that could be used as an int, but had the properties that I need. The process that I would use to do this can be called "abstraction."

+6
Jan 22 2018-11-11T00:
source share

Here is my narrow show and interpretation. This is self-evident and works in 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() 
+5
Jan 22 '11 at 4:32
source share

Other answers already give a good idea of ​​what abstractions exist. Let's look at the quotes one by one and give 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 from them.

Skip function as parameter: List(1,-2,3).map(math.abs(x)) It is clear that abs is passed as a parameter here. map itself is an abstract on a function that performs a specific special thing with each element of the list. val list = List[String]() indicates a type parameter (String). You can write a collection type that 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 "hidden" in the second method.

The consequence of our streams of events being first-class is that we can abstract over them.

In Swing, an event simply “happens” unexpectedly, and you have to deal with it here and now. Streams of events allow you to make all plumbing work by wiring in a more declarative way. For example. when you want to change the responsible listener in Swing, you need to unregister the old one and register the new one, as well as find out all the details of gory (for example, problems with threads). With event streams, the event source becomes a thing that you can simply skip, making it not very different from a byte stream or char, hence a more abstract concept.

Elements of an abstract type provide flexibility for the way to abstract from particular types of components.

The Buffer class above is already an example for this.

+2
Jan 22 '11 at 11:52
source share



All Articles