J'ai essayé de comprendre pourquoi le JDK 8 Lambda Expert Group (EG) a décidé de ne pas inclure un nouveau type de fonction dans le langage de programmation Java.
En parcourant la liste de diffusion, j'ai trouvé un fil avec la discussion sur la suppression des types de fonctions .
Beaucoup de déclarations sont ambiguës pour moi, peut-être en raison du manque de contexte et dans certains cas en raison de ma connaissance limitée de la mise en œuvre des systèmes de type.
Cependant, il y a quelques questions que je crois pouvoir formuler en toute sécurité dans ce site pour m'aider à mieux comprendre ce qu'elles signifiaient.
Je sais que je pourrais poser les questions dans la liste de diffusion, mais le fil est ancien et toutes les décisions sont déjà prises, donc il y a des chances que je sois ignoré, surtout vu que ces gars sont déjà en retard avec leurs plans.
Dans sa réponse pour soutenir la suppression des types de fonctions et approuver l'utilisation des types SAM, Brian Goetz dit:
Pas de réification. Il y avait un long fil sur l'utilité de réifier les types de fonctions. Sans réification, les types de fonction sont entravés.
Je n'ai pas pu trouver le fil qu'il mentionne. Maintenant, je peux comprendre que l'introduction d'un type de fonction structurelle peut impliquer certaines complications dans le système de type Java principalement nominal, ce que je ne peux pas comprendre, c'est comment les types SAM paramétrés sont différents en termes de réification.
Ne sont-ils pas tous les deux soumis aux mêmes problèmes de réification? Est-ce que quelqu'un comprend en quoi les types de fonctions sont différents des types SAM paramétrés en termes de réification?
Dans un autre commentaire, Goetz dit:
Il existe deux approches de base du typage: nominal et structurel. L'identité d'un nominal est basée sur son nom; l'identité d'un type structurel est basée sur ce qui le compose (comme «tuple d'int, int» ou «fonction d'int à flottant».) La plupart des langues choisissent principalement nominale ou principalement structurelle; il n'y a pas beaucoup de langues qui mélangent avec succès le typage nominal et structurel, sauf "autour des bords". Java est presque entièrement nominal (à quelques exceptions près: les tableaux sont un type structurel, mais en bas, il y a toujours un type d'élément nominal; les génériques ont également un mélange de nominal et structurel, et cela fait en fait partie de la source de nombreux des plaintes des gens sur les génériques.) Greffage d'un système de type structurel (types de fonctions) sur Java ' Le système de type nominal signifie de nouveaux cas de complexité et de bord. Les avantages des types de fonction en valent-ils la peine?
Ceux d'entre vous ayant une expérience dans la mise en œuvre de systèmes de type. Connaissez-vous des exemples de ces complexités ou cas marginaux qu'il mentionne ici?
Honnêtement, je suis confus par ces allégations quand je considère qu'un langage de programmation comme Scala, qui est entièrement basé sur la JVM, prend en charge les types structurels comme les fonctions et les tuples, même avec les problèmes de réification de la plate-forme sous-jacente.
Ne vous méprenez pas, je ne dis pas qu'un type de fonction devrait être meilleur que les types SAM. Je veux juste comprendre pourquoi ils ont pris cette décision.
la source
Réponses:
L'approche SAM est en fait quelque peu similaire à ce que Scala (et C ++ 11) font avec les fonctions anonymes (créées avec l'
=>
opérateur de Scala ou la syntaxe C ++ 11[]()
(lambda)).La première question à laquelle il faut répondre du côté de Java est de savoir si le type de retour d'une instruction lambda doit être un nouveau type primitve, comme
int
oubyte
, ou un type d'objet quelconque. Dans Scala, il n'y a pas de types primitifs - même un nombre entier est un objet de classeInt
- et les fonctions ne sont pas différents objets étant de la classeFunction1
,Function2
et ainsi de suite, selon le nombre d'arguments de la fonction prend.C ++ 11, ruby et python ont de même des expressions lambda qui retournent un objet qui peut être appelé de manière explicite ou implicite. L'objet renvoyé possède une méthode standard (telle que celle
#call
qui peut être utilisée pour l'appeler en tant que fonction. C ++ 11, par exemple, utilise unstd::function
type qui surchargeoperator()
afin que les appels de la méthode d'appel de l'objet ressemblent même textuellement à des appels de fonction. :-)Là où la nouvelle proposition pour Java devient désordonnée, c'est dans l'utilisation du typage structurel pour permettre d'assigner une telle méthode à un autre objet, comme un
Comparator
qui a une seule méthode principale avec un nom différent . Bien que ce soit sur le plan conceptuel Icky d' une certaine façon, il ne signifie pas que l'objet résultant peut être transmis à des fonctions existantes qui prennent un objet pour représenter un rappel, un comparateur, et ainsi de suite, et attendent de pouvoir appeler un seul bien défini telle que#compare
ou#callback
. L'astuce de C ++operator()
consistant à ignorer soigneusement ce problème avant même C ++ 11, car toutes ces options de rappel étaient appelables de la même manière, et donc la STL ne nécessitait aucun ajustement pour permettre aux lambdas C ++ 11 d'être utilisés avecsort
etc. Java, n'ayant pas utilisé de dénomination standard pour de tels objets dans le passé (peut-être parce qu'aucune astuce comme la surcharge des opérateurs ne rendait une approche unique évidente), n'a pas autant de chance, donc ce hack les empêche d'avoir à changer beaucoup d'API existantes .la source
java.util.Function2<T1, T2, R>
dans Java 1.0, il n'y aurait pas d'Comparator
interface, mais toutes ces méthodes prendraient aFunction2<T1, T2, int>
. (Eh bien, il y aurait eu toute une série d'interfaces comme àFunction2TT_int<T1, T2>
cause des primitives, mais vous comprenez mon point de vue.)