Polimorfizm wyższej rangi jest niezwykle przydatny. W Systemie F (podstawowy język typowanych języków FP, który znasz), jest to niezbędne do akceptowania „typowanych kodowań kościelnych”, tak jak programuje system F. Bez nich system F jest całkowicie bezużyteczny.
W Systemie F definiujemy liczby jako
Nat = forall c. (c -> c) -> c -> c
Dodawanie ma typ
plus : Nat -> Nat -> Nat
plus l r = Λ t. λ (s : t -> t). λ (z : t). l s (r s z)
który jest wyższym typem rangi ( forall c.
pojawia się w tych strzałkach).
To pojawia się także w innych miejscach. Na przykład, jeśli chcesz wskazać, że obliczenia są poprawnym stylem przekazywania kontynuacji (google „codensity haskell”), powinieneś to zrobić jako
type CPSed A = forall c. (A -> c) -> c
Nawet mówienie o niezamieszkanym typie w Systemie F wymaga polimorfizmu wyższej rangi
type Void = forall a. a
Krótko mówiąc, pisanie funkcji w systemie czystego typu (System F, CoC) wymaga polimorfizmu wyższego rzędu, jeśli chcemy poradzić sobie z interesującymi danymi.
W szczególności w systemie F kodowania te muszą być „impredykatywne”. Oznacza to, że forall a.
kwantyfikuje absolutnie wszystkie typy . Krytycznie obejmuje to właśnie ten typ, który definiujemy. W forall a. a
które a
może rzeczywiście stać na forall a. a
raz! W językach takich jak ML tak nie jest, mówi się, że są „predykatywne”, ponieważ zmienna typu kwantyfikuje tylko na podstawie zestawu typów bez kwantyfikatorów (zwanych monotypami). Nasza definicja plus
wymaganego impredicativity a także dlatego, że instancja c
w l : Nat
się Nat
!
Na koniec chciałbym wymienić ostatni powód, dla którego chcielibyście zarówno impredykatywności, jak i polimorfizmu wyższej rangi, nawet w języku z dowolnie rekurencyjnymi typami (w przeciwieństwie do systemu F). W Haskell istnieje monada efektów zwana „monadą wątku stanu”. Chodzi o to, że monada wątku stanu pozwala mutować rzeczy, ale wymaga ucieczki, aby wynik nie był zależny od niczego zmiennego. Oznacza to, że obliczenia ST są zauważalnie czyste. Aby egzekwować ten wymóg, stosujemy polimorfizm wyższego rzędu
runST :: forall a. (forall s. ST s a) -> a
Zapewniając, że a
jest to poza zakresem, w którym wprowadzamy s
, wiemy, że a
oznacza to dobrze sformułowany typ, na którym nie można polegać s
. Używamy s
do parameryzowania wszystkich zmiennych w tym konkretnym wątku stanu, więc wiemy, że a
jest on niezależny od zmiennych, a zatem nic nie wymyka się z zakresu tego ST
obliczenia! Wspaniały przykład użycia typów w celu wykluczenia źle sformułowanych programów.
Nawiasem mówiąc, jeśli chcesz poznać teorię typów, sugeruję zainwestowanie w dobrą książkę lub dwie. Trudno jest nauczyć się tych rzeczy w kawałkach. Sugerowałbym jedną z książek Pierce'a lub Harpera na temat teorii PL w ogóle (i niektórych elementów teorii typów). Książka „Zaawansowane tematy w typach i językach programowania” obejmuje również sporo teorii typów. Wreszcie „Programowanie w teorii typów Martina Lofa” to bardzo dobra ekspozycja na temat intensywnej teorii typów, którą nakreślił Martin Lof.
let sdff = (g : (f : <T> (e : T) => void) => void) => {}