Что означает "абстрагирование"?

Часто в литературе Scala я встречаю фразу "абстракция", но я не понимаю намерения. Например,, пишет Мартин Одерски

Вы можете передавать методы (или "функции" ) в качестве параметров, или вы можете абстрагироваться от них. Вы можете указать типы в качестве параметров, или вы можете абстрагироваться от них.

В качестве другого примера, в "Устаревший шаблон наблюдателя" ,

Следствием наших потоков событий, являющихся первоклассными значениями, является то, что мы можем абстрагироваться над ними.

Я прочитал, что генераторы первого порядка "абстрактные над типами", а монад "абстрактные над конструкторами типов". И мы также видим такие фразы в документе Cake Pattern. Чтобы процитировать один из многих таких примеров:

Элементы абстрактного типа обеспечивают гибкий способ абстрагирования по конкретным типам компонентов.

Даже релевантные вопросы используют эту терминологию. "не может быть экзистенциально абстрактным по параметризованному типу..."

Итак... что означает "абстрактное представление"?

Ответ 1

В алгебре, как и в повседневном формировании понятий, абстракции формируются путем группировки вещей по некоторым существенным характеристикам и исключения их специфических других характеристик. Абстракция объединяется под одним символом или словом, обозначающим сходство. Мы говорим, что абстрагируемся от различий, но на самом деле это означает, что мы интегрируем по сходству.

Например, рассмотрим программу, которая принимает сумму чисел 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)

Мы абстрагировались над моноидами и складками.

Ответ 2

В первом приближении возможность "абстрагироваться над" означает, что вместо того, чтобы использовать что-то непосредственно, вы можете сделать его параметр или использовать его "анонимно".

Scala позволяет абстрагироваться от типов, позволяя классам, методам и значениям иметь параметры типа и значения для абстрактных (или анонимных) типов.

Scala позволяет абстрагироваться от действий, позволяя методам иметь параметры функции.

Scala позволяет абстрагироваться от функций, позволяя структурам определяться структурно.

Scala позволяет абстрагироваться от параметров типа, задавая параметры типа более высокого порядка.

Scala позволяет вам абстрагироваться от шаблонов доступа к данным, позволяя создавать экстракторы.

Scala позволяет абстрагироваться от "вещей, которые могут быть использованы как что-то еще", разрешая неявные преобразования в качестве параметров. Haskell делает аналогично классам типов.

Scala (пока) не позволяет абстрагироваться от классов. Вы не можете передать класс чему-то, а затем использовать этот класс для создания новых объектов. Другие языки позволяют абстрагироваться по классам.

( "Monads abstract over type constructors" применим только в очень ограничительном смысле. Не зацикливайтесь на нем, пока не появится ваш "Aha! Я понимаю монад!" ).

Возможность абстрагироваться по некоторым аспектам вычислений - это в основном то, что позволяет повторно использовать код и позволяет создавать библиотеки функциональности. Scala позволяет абстрагироваться больше других типов, чем другие основные языки, а библиотеки в Scala могут быть соответственно более мощными.

Ответ 3

Абстракция - своего рода обобщение.

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

Не только в Scala, но и во многих языках необходимо иметь такие механизмы для уменьшения сложности (или, по крайней мере, создать иерархию, которая разделяет информацию на более понятные части).

Класс - это абстракция над простым типом данных. Это похоже на базовый тип, но фактически обобщает их. Таким образом, класс представляет собой нечто большее, чем простой тип данных, но имеет много общего с ним.

Когда он говорит "абстрагирование", он означает процесс, которым вы обобщаете. Поэтому, если вы абстрагируетесь над методами в качестве параметров, вы обобщаете процесс этого. например, вместо того, чтобы передавать методы в функции, вы можете создать некоторый тип обобщенного способа его обработки (например, не передавать методы вообще, а создавать специальную систему для борьбы с ним).

В этом случае он конкретно означает процесс абстрагирования проблемы и создания такого решения проблемы. C имеет очень мало возможностей абстрагироваться (вы можете это сделать, но он становится беспорядочным, и язык не поддерживает его напрямую). Если вы написали его на С++, вы могли бы использовать концепции oop, чтобы уменьшить сложность проблемы (ну, такая же сложность, но концептуализация вообще проще (по крайней мере, как только вы научитесь думать с точки зрения абстракций)).

Например, если мне нужен специальный тип данных, похожий на int, но, допустим, я мог бы абстрагироваться от него, создав новый тип, который можно было бы использовать как int, но имел те свойства, которые мне нужны. Процесс, который я использовал бы, чтобы сделать такое, можно назвать "абстрагированием".

Ответ 4

Вот мое узкое шоу и интерпретация. Это самоочевидно и работает в 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

Другие ответы дают уже хорошее представление о том, какие существуют абстракции. Давайте рассмотрим кавычки один за другим и дадим пример:

Вы можете передавать методы (или "функции" ) как параметры, или вы можете абстрагироваться Над ними. Вы можете указать типы как параметров, или вы можете абстрагироваться от их.

Функция пропуска в качестве параметра: 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, вам нужно отменить регистрацию старого и зарегистрировать новый, а также узнать все детали gory (например, проблемы с потоками). С потоками событий источником событий становится вещь, которую вы можете просто пропустить, делая ее не очень отличной от потока байтов или char, следовательно, более абстрактной концепции.

Элементы абстрактного типа обеспечивают гибкость способ абстрагироваться от конкретных типов компоненты.

Класс Buffer выше уже является примером для этого.

Ответ 6

Ответы выше дают отличное объяснение, но, подытожив его одним предложением, я бы сказал:

Абстрагироваться от чего-либо - это то же самое, что пренебрегать этим, если оно не имеет значения.