Co oznacza "abstrakt nad"?

Często w literaturze Scali spotykam się ze zwrotem" abstrakt nad", ale nie rozumiem intencji. na przykład , Martin Odersky pisze

Możesz przekazać metody (lub "funkcje") jako parametry, lub możesz abstrakt nad nimi. Można określić typy jako parametry, lub można streszczenie nad nich.

Jako kolejny przykład, w "Deprecating The Observer Pattern" papier,

A konsekwencją tego, że nasze strumienie zdarzeń są wartościami pierwszej klasy jest to, że możemy abstrakt nad nimi.

Czytałem, że generyki pierwszego rzędu "abstrakcyjne nad typami" , podczas gdy monady "abstrakcyjne nad konstruktorami typów". I widzimy również takie zwroty w Cake Pattern paper. Zacytuję jeden z wielu takich przykładów:

Pręty typu abstrakcyjnego zapewniają elastyczną drogę do abstrakcji nad konkretnych typów elementów.

Nawet istotne pytania o przepełnienie stosu używają tej terminologii. "nie może egzystencjalnie abstrakcyjnie nad parametryzowanym typem..."

Więc... co właściwie oznacza "abstrakt nad"?

Author: asthasr, 2011-01-22

6 answers

W algebrze, podobnie jak w codziennym tworzeniu pojęć, abstrakcje są tworzone przez grupowanie rzeczy według pewnych podstawowych cech i pomijanie ich specyficznych innych cech. Abstrakcja jest ujednolicona pod jednym symbolem lub słowem oznaczającym podobieństwa. Mówimy, że abstraktujemy nad różnicami, ale to naprawdę oznacza, że jesteśmy integrujemy przez podobieństwa.

Na przykład, rozważmy program, który przyjmuje sumę liczb 1, 2, oraz 3:

val sumOfOneTwoThree = 1 + 2 + 3

Ten program nie jest zbyt interesujący, ponieważ nie jest zbyt abstrakcyjny. Możemy abstrakt nad liczbami, które sumujemy, integrując wszystkie listy liczb pod jednym symbolem ns:

def sumOf(ns: List[Int]) = ns.foldLeft(0)(_ + _)
I nie obchodzi nas, że to lista. List jest specyficznym konstruktorem typu (pobiera typ i zwraca typ), ale możemy abstrakt nad konstruktorem typu, określając, którą istotną cechę chcemy (że może być "składany"): {]}
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)

I możemy mieć ukryte Foldable instancje dla List i każdą inną rzecz, którą możemy spasować.

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

Co więcej, możemy abstrakt nad zarówno operacją, jak i typem operandów:

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)

Teraz mamy coś całkiem ogólnego. Metoda mapReduce złoży dowolny F[A], ponieważ możemy udowodnić, że F jest składalny i że {[15] } jest monoidem lub może być zmapowana do jednego. Na przykład:

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)

Mamy streszczenie monoidy i składanki.

 124
Author: Apocalisp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-19 18:21:02

Dla pierwszego przybliżenia, możliwość "abstrakcji nad" czymś oznacza, że zamiast używać tego czegoś bezpośrednio, możesz zrobić z tego parametr lub w inny sposób użyć go "anonimowo".

Scala pozwala na abstrakcyjne typy, pozwalając klasom, metodom i wartościom mieć parametry typu, A wartości abstrakcyjne (lub anonimowe) typy.

Scala pozwala na abstrakcję nad akcjami, pozwalając metodom na posiadanie parametrów funkcji.

Scala pozwala na abstrakcyjny nad cechami, pozwalając definiować typy strukturalnie.

Scala pozwala na abstrakcję nad parametrami typu, pozwalając na parametry typu wyższego rzędu.

Scala pozwala na abstrakcję wzorców dostępu do danych, pozwalając na tworzenie ekstraktorów.

Scala pozwala na abstrakcję nad "rzeczami, które mogą być używane jako coś innego", zezwalając na niejawne konwersje jako parametry. Haskell robi podobnie z klasami typu.

Scala nie pozwala (jeszcze) ty do abstrakcji nad klasami. Nie można przekazać klasy do czegoś, a następnie użyć tej klasy do tworzenia nowych obiektów. Inne języki pozwalają na abstrakcję nad klasami.

("monady abstrakcyjne nad konstruktorami typów" są prawdziwe tylko w bardzo restrykcyjny sposób. Nie daj się zawiesić, dopóki nie będziesz miał swojego " Aha! Rozumiem monady!!"moment.)

Umiejętność abstrakcji nad pewnym aspektem obliczeń jest w zasadzie tym, co pozwala na ponowne użycie kodu i umożliwia tworzenie bibliotek funkcjonalność. Scala pozwala na wiele więcej rodzajów rzeczy do abstrahowania niż więcej języków głównego nurtu, a biblioteki w Scali mogą być odpowiednio potężniejsze.

 11
Author: Dave Griffith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-22 02:14:32

Abstrakcja jest rodzajem uogólnienia.

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

Nie tylko w Scali, ale w wielu językach istnieje potrzeba posiadania takich mechanizmów, aby zmniejszyć złożoność (lub przynajmniej stworzyć hierarchię dzielącą informacje na łatwiejsze do zrozumienia fragmenty).

Klasa jest abstrakcją nad prostym typem danych. Jest to coś w rodzaju podstawowego typu, ale w rzeczywistości je uogólnia. Tak więc klasa jest czymś więcej niż prostym typem danych, ale ma wiele rzeczy z nim wspólne.

Kiedy mówi "abstrakcja", ma na myśli proces, za pomocą którego uogólniasz. Jeśli więc abstrakcjonizujesz metody jako parametry, uogólniasz proces robienia tego. na przykład, zamiast przekazywania metod do funkcji możesz stworzyć jakiś rodzaj uogólnionego sposobu obsługi(np. nie przekazywanie metod w ogóle, ale budowanie specjalnego systemu do radzenia sobie z nimi).

W tym przypadku konkretnie chodzi o proces abstrakcji problemu i tworzenie oop jak rozwiązanie problemu. C ma bardzo małą zdolność do abstrakcji(można to zrobić, ale robi się bałagan bardzo szybko i język nie obsługuje go bezpośrednio). Jeśli napisałeś go w C++, możesz użyć pojęć oop, aby zmniejszyć złożoność problemu(cóż, jest to ta sama złożoność, ale konceptualizacja jest ogólnie łatwiejsza(przynajmniej raz nauczysz się myśleć w kategoriach abstrakcji)).

Na przykład, jeśli potrzebowałem specjalnego typu danych, który był jak int, ale powiedzmy ograniczony I czy abstrakt nad nim, tworząc nowy typ, który może być używany jak int, ale miał te właściwości, których potrzebowałem. Proces, którego użyłbym do zrobienia takiej rzeczy, nazwałby się "abstrakcją".

 6
Author: AbstractDissonance,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-22 01:41:13

Oto moja wąska interpretacja show I tell. To jest oczywiste i działa w 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
Author: huynhjl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-22 04:32:16

Inne odpowiedzi dają już dobre wyobrażenie o tym, jakie rodzaje abstrakcji istnieją. Przejdźmy do cudzysłowów jeden po drugim i podaj przykład:

Można przekazać metody (lub " funkcje") jako parametry, lub można abstrakcyjne nad nimi. Możesz określić typy jako parametrów, albo można nad oni.

Funkcja Pass jako parametr: List(1,-2,3).map(math.abs(x)) wyraźnie abs jest tutaj przekazywana jako parametr. map sama abstrakcja nad funkcją wykonującą pewien specjalny rzecz z każdym elementem listy. val list = List[String]() określa typ paramter (String). Możesz napisać typ kolekcji, który zamiast tego używa elementów typu abstrakcyjnego: val buffer = Buffer{ type Elem=String }. Jedna różnica polega na tym, że musisz napisać def f(lis:List[String])..., ale def f(buffer:Buffer)..., więc Typ elementu jest jakby "ukryty" w drugiej metodzie.

Konsekwencja naszych transmisji wydarzeń bycie wartościami pierwszej klasy polega na tym, że my może nad nimi dominować.

W huśtawce zdarzenie po prostu "dzieje się" znikąd i musisz sobie z tym poradzić tutaj i teraz. Strumienie zdarzeń pozwalają wykonać wszystkie instalacje hydrauliczne i okablowanie w bardziej deklaratywny sposób. Np. gdy chcesz zmienić odpowiedzialnego słuchacza w swingu, musisz wyrejestrować Starego i zarejestrować nowego, a także znać wszystkie szczegóły (np. wątki). W przypadku strumieni zdarzeń, źródło zdarzeń staje się rzeczą, którą można po prostu przekazać, dzięki czemu nie różni się zbytnio od strumienia bajtów lub znaków, stąd bardziej "abstrakcyjna" koncepcja.

Typ abstrakcyjny członkowie zapewniają elastyczne sposób na abstrakcję nad konkretnymi rodzajami komponenty.

Powyższa Klasa bufora jest już tego przykładem.

 2
Author: Landei,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-22 11:52:38

Odpowiedzi powyżej dostarczają doskonałego wyjaśnienia, ale podsumowując je w jednym zdaniu, powiedziałbym:

abstrakcja nad czymś jest tym samym, co zaniedbywanie tego tam, gdzie nie ma znaczenia .

 1
Author: Michał Kaczanowicz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-10-04 10:28:39