Da niemand sonst die Frage beantwortet hat, denke ich, dass ich es selbst versuchen werde. Ich muss ein bisschen philosophisch werden.
Bei der generischen Programmierung geht es darum, ähnliche Typen zu abstrahieren, ohne dass Typinformationen verloren gehen (was beim objektorientierten Wertepolymorphismus der Fall ist). Zu diesem Zweck müssen die Typen notwendigerweise eine bestimmte Art von Schnittstelle (eine Reihe von Operationen, nicht den OO-Begriff) gemeinsam verwenden, die Sie verwenden können.
In objektorientierten Sprachen erfüllen Typen eine Schnittstelle durch Klassen. Jede Klasse hat eine eigene Schnittstelle, die als Teil ihres Typs definiert ist. Da alle Klassen List<T>
dieselbe Schnittstelle verwenden, können Sie Code schreiben, der unabhängig von T
Ihrer Auswahl funktioniert . Eine andere Möglichkeit, eine Schnittstelle zu erzwingen, ist eine Vererbungsbeschränkung, und obwohl die beiden unterschiedlich zu sein scheinen, ähneln sie sich, wenn Sie darüber nachdenken.
In den meisten objektorientierten Sprachen List<>
ist es kein richtiger Typ für sich. Es hat keine Methoden und somit keine Schnittstelle. Nur List<T>
das hat diese Dinge. Technisch gesehen sind die einzigen Typen, über die Sie sinnvoll abstrahieren können, die mit der Art *
. Um höherwertige Typen in einer objektorientierten Welt zu verwenden, müssen Sie Typeinschränkungen in einer Weise formulieren, die mit dieser Einschränkung vereinbar ist.
Zum Beispiel können wir, wie in den Kommentaren erwähnt, anzeigen Option<>
und List<>
als "abbildbar" in dem Sinne, dass Sie, wenn Sie eine Funktion haben, eine Option<T>
in eine Option<S>
oder eine List<T>
in eine umwandeln könnten List<S>
. Denken Sie daran, dass Klassen nicht verwendet werden können, um über höherwertige Typen direkt zu abstrahieren. Stattdessen erstellen wir eine Schnittstelle:
IMappable<K<_>, T> where K<T> : IMappable<K<_>, T>
Und dann implementieren wir die Schnittstelle in beide List<T>
und Option<T>
als IMappable<List<_>, T>
undIMappable<Option<_>, T>
jeweils. Was wir getan haben, ist die Verwendung höherwertiger Typen, um Einschränkungen für die tatsächlichen (nicht höherwertigen) Typen Option<T>
und zu setzen List<T>
. So wird es in Scala gemacht, obwohl Scala natürlich Funktionen wie Eigenschaften, Typvariablen und implizite Parameter hat, die es aussagekräftiger machen.
In anderen Sprachen ist es möglich, über höherwertige Typen direkt zu abstrahieren. In Haskell, einer der höchsten Autoritäten für Typsysteme, können wir eine Typklasse für jeden Typ formulieren, auch wenn er eine höhere Art hat. Beispielsweise,
class Mappable mp where
map :: mp a -> mp b
Hierbei handelt es sich um eine Einschränkung, die direkt auf einen (nicht angegebenen) Typ mp
angewendet wird, der einen Typparameter annimmt, und erfordert, dass dieser mit der Funktion verknüpft ist, die aus map
einem mp<a>
in einen verwandeltmp<b>
. Wir können dann Funktionen schreiben, die höherwertige Typen einschränken, indem Sie Mappable
genau wie in objektorientierten Sprachen eine Vererbungsbeschränkung platzieren. Naja, so ungefähr.
Zusammenfassend gesagt hängt Ihre Fähigkeit, höherwertige Typen zu verwenden, von Ihrer Fähigkeit ab, sie zu beschränken oder als Teil von Typbeschränkungen zu verwenden.