Pozwolę sobie nadrobić zaległości w tym zamieszaniu, wprowadzając z pewnym niedowierzaniem. Lubię używać analogii do poziomu wartości, aby to wyjaśnić, ponieważ ludzie są bardziej zaznajomieni z tym.
Konstruktor typu to typ, który można zastosować do argumentów typu w celu „skonstruowania” typu.
Konstruktor wartości to wartość, którą można zastosować do argumentów wartości w celu „skonstruowania” wartości.
Konstruktory wartości są zwykle nazywane „funkcjami” lub „metodami”. O tych „konstruktorach” mówi się również, że są „polimorficzne” (ponieważ można je stosować do konstruowania „elementów” o różnym „kształcie”) lub „abstrakcjach” (ponieważ abstrakcyjne są to, co różni się między różnymi instancjami polimorficznymi).
W kontekście abstrakcji / polimorfizmu pierwszeństwo odnosi się do „jednorazowego użycia” abstrakcji: raz abstraktujesz nad typem, ale sam ten typ nie może abstrakcji nad niczym. Generics Java 5 są pierwszego rzędu.
Interpretacja powyższych charakterystyk abstrakcji pierwszego rzędu to:
Konstruktor typów to typ, który można zastosować do argumentów odpowiedniego typu, aby „skonstruować” odpowiedni typ.
Konstruktor wartości to wartość, którą można zastosować do odpowiednich argumentów wartości w celu „skonstruowania” właściwej wartości.
Aby podkreślić, że nie ma w tym żadnej abstrakcji (myślę, że można to nazwać „zerowym porządkiem”, ale nigdzie go nie widziałem), na przykład wartość 1
lub typ String
, zwykle mówimy, że coś jest „właściwą” wartością lub typem.
Właściwa wartość jest „natychmiast użyteczna” w tym sensie, że nie czeka na argumenty (nie jest nad nimi abstrakcyjna). Pomyśl o nich jako o wartościach, które możesz łatwo wydrukować / sprawdzić (serializacja funkcji to oszustwo!).
Właściwy typ to typ, który klasyfikuje wartości (w tym konstruktory wartości), konstruktory typów nie klasyfikują żadnych wartości (najpierw należy je zastosować do argumentów odpowiedniego typu, aby uzyskać odpowiedni typ). Aby utworzyć instancję typu, konieczne (ale niewystarczające) jest, aby był on właściwym typem. (Może to być klasa abstrakcyjna lub klasa, do której nie masz dostępu).
„Nadrzędny porządek” jest po prostu ogólnym terminem, który oznacza wielokrotne stosowanie polimorfizmu / abstrakcji. Oznacza to to samo dla typów i wartości polimorficznych. Konkretnie, abstrakcja wyższego rzędu abstraktuje nad czymś, co abstraktuje nad czymś. W przypadku typów termin „bardziej uprzywilejowany” jest specjalną wersją bardziej ogólnego „wyższego rzędu”.
Tak więc wersja naszej charakteryzacji wyższego rzędu staje się:
Konstruktor typu to typ, który można zastosować do argumentów typu (odpowiednie typy lub konstruktory typu), aby „skonstruować” odpowiedni typ (konstruktor).
Konstruktor wartości to wartość, którą można zastosować do argumentów wartości (prawidłowe wartości lub konstruktory wartości), aby „skonstruować” odpowiednią wartość (konstruktor).
Zatem „wyższy porządek” oznacza po prostu, że kiedy mówisz „abstrakcja nad X”, naprawdę masz na myśli! To, X
co jest abstrakcyjne, nie traci własnych „praw do abstrakcji”: może abstrakcji wszystkiego, co chce. (Nawiasem mówiąc, używam tutaj czasownika „streszczenie”, aby pominąć coś, co nie jest niezbędne do zdefiniowania wartości lub typu, aby użytkownik mógł je zmienić / podać jako argument jako argument .)
Oto kilka przykładów (zainspirowanych pytaniami Lutza wysłanymi e-mailem) prawidłowych wartości i typów pierwszego i wyższego rzędu:
proper first-order higher-order
values 10 (x: Int) => x (f: (Int => Int)) => f(10)
types (classes) String List Functor
types String ({type λ[x] = x})#λ ({type λ[F[x]] = F[String]})#λ
Gdzie używane klasy zostały zdefiniowane jako:
class String
class List[T]
class Functor[F[_]]
Aby uniknąć pośrednictwa poprzez definiowanie klas, musisz w jakiś sposób wyrazić anonimowe funkcje typu, które nie są wyrażalne bezpośrednio w Scali, ale możesz używać typów strukturalnych bez nadmiernego nakładu składniowego ( #λ
styl jest spowodowany https://stackoverflow.com / users / 160378 / retronym afaik ):
W niektórych hipotetycznych przyszłych wersjach Scali, które obsługują anonimowe funkcje typu, możesz skrócić ten ostatni wiersz z przykładów do:
types (informally) String [x] => x [F[x]] => F[String]) // I repeat, this is not valid Scala, and might never be
(Osobiście żałuję, że kiedykolwiek mówiłem o „typach lepszych”, to w końcu są to tylko typy! Gdy absolutnie potrzebujesz ujednoznacznić, sugeruję powiedzenie takich rzeczy, jak „parametr konstruktora typu”, „członek konstruktora typu” lub „alias konstruktora typów”, aby podkreślić, że nie mówimy tylko o odpowiednich typach).
ps: Aby jeszcze bardziej skomplikować sprawy, „polimorficzny” jest niejednoznaczny w inny sposób, ponieważ typ polimorficzny oznacza czasami typ uniwersalnie skwantyfikowany, taki jak Forall T, T => T
, który jest właściwym typem, ponieważ klasyfikuje wartości polimorficzne (w Scali ta wartość może być zapisane jako typ konstrukcyjny {def apply[T](x: T): T = x}
)