Ponieważ nikt inny nie odpowiedział na to pytanie, myślę, że spróbuję. Będę musiał trochę filozofować.
Programowanie ogólne polega na abstrakcji nad podobnymi typami, bez utraty informacji o typie (co dzieje się z polimorfizmem wartości obiektowych). Aby to zrobić, typy muszą koniecznie współdzielić jakiś interfejs (zestaw operacji, a nie termin OO), którego można użyć.
W językach obiektowych typy spełniają interfejs dzięki klasom. Każda klasa ma własny interfejs zdefiniowany jako część swojego typu. Ponieważ wszystkie klasy List<T>
mają ten sam interfejs, możesz pisać kod, który działa bez względu na to, co T
wybierzesz. Innym sposobem nałożenia interfejsu jest ograniczenie dziedziczenia i chociaż oba wydają się różne, są one trochę podobne, jeśli się nad tym zastanowić.
W większości języków obiektowych List<>
sam w sobie nie jest właściwym typem. Nie ma metod, a zatem nie ma interfejsu. Tylko List<T>
to ma te rzeczy. Zasadniczo, z bardziej technicznego punktu widzenia, jedynymi typami, które można znacząco wyodrębnić, są te z tym rodzajem *
. Aby korzystać z typów lepiej dobranych w świecie zorientowanym obiektowo, musisz sformułować ograniczenia typu w sposób zgodny z tym ograniczeniem.
Na przykład, jak wspomniano w komentarzach, możemy przeglądać Option<>
i List<>
„mapować”, w tym sensie, że jeśli masz funkcję, możesz przekonwertować Option<T>
na an Option<S>
lub List<T>
na List<S>
. Pamiętając, że klasy nie mogą być używane do abstrakcyjnego nad typami wyższego rodzaju, zamiast tego tworzymy interfejs:
IMappable<K<_>, T> where K<T> : IMappable<K<_>, T>
A następnie zaimplementować interfejs zarówno List<T>
a Option<T>
jak IMappable<List<_>, T>
i IMappable<Option<_>, T>
odpowiednio. To, co zrobiliśmy, polega na użyciu typów o wyższym rodzaju, aby nałożyć ograniczenia na rzeczywiste typy (o innym rodzaju) Option<T>
i List<T>
. Tak to się dzieje w Scali, chociaż oczywiście Scala ma takie cechy, jak cechy, zmienne typu i ukryte parametry, które czynią ją bardziej wyrazistą.
W innych językach można bezpośrednio wyodrębnić typy wyższego rodzaju. W Haskell, jednym z najwyższych uprawnień w systemach typów, możemy sformułować klasę typu dla dowolnego typu, nawet jeśli ma on wyższy rodzaj. Na przykład,
class Mappable mp where
map :: mp a -> mp b
Jest to ograniczenie nakładane bezpośrednio na (nieokreślony) typ, mp
który przyjmuje jeden parametr typu i wymaga, aby był on powiązany z funkcją, map
która zmienia an mp<a>
w mp<b>
. Następnie możemy napisać funkcje ograniczające typy wyższego rzędu, Mappable
tak jak w językach zorientowanych obiektowo można wprowadzić ograniczenie dziedziczenia. Cóż, w pewnym sensie.
Podsumowując, twoja umiejętność korzystania z typów o wyższym rodzaju zależy od twojej zdolności do ich ograniczenia lub użycia ich jako części ograniczeń typów.