Ich habe versucht zu verstehen, warum die JDK 8 Lambda Expert Group (EG) beschlossen hat, keinen neuen Funktionstyp in die Java-Programmiersprache aufzunehmen.
Beim Durchgehen der Mailingliste fand ich einen Thread mit der Diskussion über das Entfernen von Funktionstypen .
Viele der Aussagen sind für mich zweideutig, möglicherweise wegen des Mangels an Kontext und in einigen Fällen wegen meines begrenzten Wissens über die Implementierung von Typensystemen.
Es gibt jedoch einige Fragen, die ich meiner Meinung nach sicher auf dieser Website formulieren kann, damit ich besser verstehe, was sie bedeuten.
Ich weiß, dass ich die Fragen in der Mailingliste stellen könnte, aber der Thread ist alt und alle Entscheidungen sind bereits getroffen, so dass die Wahrscheinlichkeit groß ist, dass ich ignoriert werde, vor allem, dass diese Jungs bereits mit ihren Plänen in Verzug geraten.
In seiner Antwort, die Entfernung von Funktionstypen zu unterstützen und die Verwendung von SAM-Typen zu befürworten, sagt Brian Goetz:
Keine Bestätigung. Es gab einen langen Thread darüber, wie nützlich es sein würde, Funktionstypen zu reifizieren. Ohne Reifizierung werden Funktionstypen gehumpelt.
Ich konnte den Thread, den er erwähnt, nicht finden. Jetzt kann ich verstehen, dass die Einführung eines strukturellen Funktionstyps gewisse Komplikationen im Java-System mit zumeist nominalen Typen mit sich bringen kann. Was ich nicht verstehen kann, ist, wie sich parametrisierte SAM-Typen in Bezug auf die Reifizierung unterscheiden.
Haben nicht beide die gleichen Probleme bei der Wiedervereinigung? Versteht jemand, wie sich Funktionstypen von parametrisierten SAM-Typen in Bezug auf die Reifizierung unterscheiden?
In einem anderen Kommentar sagt Goetz:
Es gibt zwei grundlegende Ansätze für die Typisierung: nominal und strukturell. Die Identität eines Nominalen basiert auf seinem Namen; Die Identität eines Strukturtyps hängt davon ab, woraus er besteht (z. B. "Tupel von int, int" oder "Funktion von int bis float"). Die meisten Sprachen wählen meistens nominale oder meistens strukturelle; Es gibt nicht viele Sprachen, in denen die nominelle und die strukturelle Typisierung mit Ausnahme von "um die Kanten" erfolgreich gemischt werden. Java ist fast vollständig nominell (mit wenigen Ausnahmen: Arrays sind ein Strukturtyp, aber am Ende steht immer ein nomineller Elementtyp; Generika haben auch eine Mischung aus nominell und strukturell, und dies ist in der Tat ein Teil der Quelle vieler von Beschwerden der Menschen über Generika.) Pfropfen eines Strukturtypsystems (Funktionstypen) auf Java ' Das nominelle Typensystem bedeutet neue Komplexität und Randfälle. Lohnt sich der Nutzen von Funktionstypen?
Diejenigen von Ihnen, die Erfahrung in der Implementierung von Typsystemen haben. Kennen Sie Beispiele für diese Komplexität oder Randfälle, die er hier erwähnt?
Ehrlich gesagt, verwirren mich diese Behauptungen, wenn ich bedenke, dass eine Programmiersprache wie Scala, die vollständig auf der JVM basiert, strukturelle Typen wie Funktionen und Tupel unterstützt, selbst bei den Reifizierungsproblemen der zugrunde liegenden Plattform.
Verstehen Sie mich nicht falsch, ich sage nicht, dass ein Funktionstyp besser sein sollte als die SAM-Typen. Ich möchte nur verstehen, warum sie diese Entscheidung getroffen haben.
quelle
Antworten:
Der SAM-Ansatz ähnelt der Vorgehensweise von Scala (und C ++ 11) mit anonymen Funktionen (die mit dem
=>
Operator von Scala oder der[]()
(Lambda) -Syntax von C ++ 11 erstellt wurden ).Die erste Frage, die auf der Java-Seite beantwortet werden muss, ist, ob der Rückgabetyp einer Lambda-Anweisung ein neuer primitiver Typ wie
int
oderbyte
oder ein Objekttyp irgendeiner Art sein soll. In Scala gibt es keine primitiven Typen - selbst eine Ganzzahl ist ein Objekt der KlasseInt
- und Funktionen unterscheiden sich nicht, da sie Objekte der KlasseFunction1
sindFunction2
, und so weiter, abhängig von der Anzahl der Argumente, die die Funktion annimmt.C ++ 11, Ruby und Python haben ebenfalls Lambda-Ausdrücke, die ein Objekt zurückgeben, das explizit oder implizit aufrufbar ist. Das zurückgegebene Objekt verfügt über eine Standardmethode (z. B. eine Methode, mit
#call
der es als Funktion aufgerufen werden kann. In C ++ 11 wird beispielsweise einstd::function
Typ verwendet, der überladen wird,operator()
sodass Aufrufe der Aufrufmethode des Objekts sogar textuell wie Funktionsaufrufe aussehen . :-)Der neue Vorschlag für Java besteht darin, eine strukturelle Typisierung zu verwenden, um die Zuweisung einer solchen Methode zu einem anderen Objekt zu ermöglichen, beispielsweise zu einem Objekt,
Comparator
das eine einzige Hauptmethode mit einem anderen Namen hat . Obwohl dies in gewisser Hinsicht konzeptionell schwierig ist, bedeutet dies, dass das resultierende Objekt an vorhandene Funktionen übergeben werden kann, die ein Objekt zur Darstellung eines Rückrufs, eines Komparators usw. benötigen und erwarten, dass sie eine wohldefinierte Single aufrufen können Methode wie#compare
oder#callback
. Der Trick von C ++,operator()
dieses Problem zu überschreiben, hat dieses Problem bereits vor C ++ 11 sauber umgangen, da alle derartigen Rückrufoptionen auf dieselbe Weise aufgerufen werden konnten und die STL daher keine Anpassung erforderte, um die Verwendung von C ++ 11-Lambdas zuzulassensort
und so weiter. Java, das in der Vergangenheit keine Standardbenennung für solche Objekte verwendet hat (vielleicht, weil kein Trick wie das Überladen von Operatoren einen einzigen Ansatz offensichtlich machte), ist nicht so glücklich, und so verhindert dieser Hack, dass sie eine ganze Menge bestehender APIs ändern müssen .quelle
java.util.Function2<T1, T2, R>
in Java 1.0 eine gegeben hätte, gäbe es keineComparator
Schnittstelle, stattdessen würden alle diese Methoden eine annehmenFunction2<T1, T2, int>
. (Nun, es hätte eine ganze Reihe von Schnittstellen gegeben, zum BeispielFunction2TT_int<T1, T2>
aufgrund von Primitiven, aber Sie verstehen, worum es geht.)