Pourquoi for-each a deux points au lieu de «in»?

9

Depuis le guide du langage Java 5 :

Lorsque vous voyez les deux points (:), lisez-le comme "in".

Pourquoi ne pas utiliser inen premier lieu alors?

Cela me dérange depuis des années. Parce que c'est incompatible avec le reste de la langue. Par exemple, en Java il y a implements, extends, superpour les relations entre les types de symboles au lieu comme en C ++, Scala ou Ruby.

En Java deux points utilisé dans 5 contextes . Trois d'entre eux sont hérités de C. Et deux autres ont été approuvés par Joshua Bloch. Du moins, a-t-il dit lors de la conférence "La controverse sur les fermetures" . Cela se produit lorsqu'il critique l'utilisation d'un deux-points pour le mappage comme incompatible avec la sémantique de for-each. Ce qui me semble étrange parce que ce sont les modèles attendus pour chaque abus. Comme list_name/category: elementsou laberl/term: meaning.

J'ai fouillé jcp et jsr, mais je n'ai trouvé aucun signe de liste de diffusion. Google n'a trouvé aucune discussion à ce sujet. Seuls les débutants confondus par la signification de deux points en for.


Principaux arguments contre infournis jusqu'à présent:

  • nécessite un nouveau mot-clé; et
  • complique le lexisme.

Regardons les définitions de grammaire pertinentes :

déclaration
    : instruction 'for' '(' forControl ')'
    | ...
    ;

forControl
    : EnhancedForControl
    | forInit? ";" expression? ";" forUpdate?
    ;

EnhancedForControl
    : variableModifier * type variableDeclaratorId ':' expression
    ;

Changez de :pour inne pas apporter de complexité supplémentaire ou nécessite un nouveau mot clé.

user2418306
la source
1
La meilleure source pour découvrir les motivations des concepteurs de langage est souvent les concepteurs eux-mêmes. Cela dit, ce n'est apparemment que du sucre syntaxique sur un itérable; voir stackoverflow.com/questions/11216994/…
Robert Harvey

Réponses:

8

Les analyseurs normaux tels qu'ils sont généralement enseignés ont une étape de lexer avant que l'analyseur touche l'entrée. Le lexer (également «scanner» ou «tokenizer») coupe l'entrée en petits jetons annotés avec un type. Cela permet à l'analyseur principal d'utiliser des jetons comme éléments terminaux plutôt que d'avoir à traiter chaque personnage comme un terminal, ce qui entraîne des gains d'efficacité notables. En particulier, le lexer peut également supprimer tous les commentaires et espaces blancs. Cependant, une phase de tokenizer distincte signifie que les mots-clés ne peuvent pas également être utilisés comme identifiants (à moins que le langage ne prenne en charge le stropping qui est quelque peu tombé en disgrâce, ou préfixe tous les identifiants avec un sigil $foo).

Pourquoi? Supposons que nous ayons un simple jeton qui comprend les jetons suivants:

FOR = 'for'
LPAREN = '('
RPAREN = ')'
IN = 'in'
IDENT = /\w+/
COLON = ':'
SEMICOLON = ';'

Le tokenizer correspondra toujours au jeton le plus long et préférera les mots clés aux identifiants. Alors interestingsera lexed comme IDENT:interesting, mais insera lexed que IN, jamais IDENT:interesting. Un extrait de code comme

for(var in expression)

sera traduit dans le flux de jetons

FOR LPAREN IDENT:var IN IDENT:expression RPAREN

Jusqu'à présent, cela fonctionne. Mais toute variable inserait lexée comme mot clé INplutôt que comme variable, ce qui casserait le code. Le lexer ne conserve aucun état entre les jetons, et ne peut pas savoir que cela indevrait généralement être une variable sauf lorsque nous sommes dans une boucle for. De plus, le code suivant doit être légal:

for(in in expression)

Le premier inserait un identifiant, le second serait un mot-clé.

Il y a deux réactions à ce problème:

Les mots clés contextuels prêtent à confusion, réutilisons plutôt les mots clés.

Java a de nombreux mots réservés, dont certains n'ont aucune utilité, sauf pour fournir des messages d'erreur plus utiles aux programmeurs passant à Java à partir de C ++. L'ajout de nouveaux mots clés rompt le code. L'ajout de mots clés contextuels est source de confusion pour le lecteur du code, à moins qu'ils ne présentent une bonne mise en évidence de la syntaxe, et rend l'outillage difficile à implémenter car ils devront utiliser des techniques d'analyse plus avancées (voir ci-dessous).

Lorsque nous voulons étendre la langue, la seule approche sensée consiste à utiliser des symboles qui n'étaient pas légaux auparavant dans la langue. En particulier, il ne peut s'agir d'identifiants. Avec la syntaxe de boucle foreach, Java a réutilisé le :mot-clé existant avec une nouvelle signification. Avec lambdas, Java a ajouté un ->mot - clé qui ne pouvait pas apparaître auparavant dans un programme légal ( -->serait toujours lexé comme '--' '>'étant légal, et ->aurait pu être lexé auparavant '-', '>', mais cette séquence serait rejetée par l'analyseur).

Les mots clés contextuels simplifient les langues, implémentons-les

Les Lexers sont incontestablement utiles. Mais au lieu d'exécuter un lexer avant l'analyseur, nous pouvons les exécuter en tandem avec l'analyseur. Les analyseurs ascendants connaissent toujours l'ensemble des types de jetons qui seraient acceptables à n'importe quel emplacement donné. L'analyseur peut alors demander au lexer de faire correspondre l'un de ces types à la position actuelle. Dans une boucle for-each, l'analyseur serait à la position indiquée ·dans la grammaire (simplifiée) une fois la variable trouvée:

for_loop = for_loop_cstyle | for_each_loop
for_loop_cstyle = 'for' '(' declaration · ';' expression ';' expression ')'
for_each_loop = 'for' '(' declaration · 'in' expression ')'

À cette position, les jetons légaux sont SEMICOLONou IN, mais pas IDENT. Un mot in- clé serait totalement sans ambiguïté.

Dans cet exemple particulier, les analyseurs descendants n'auraient pas de problème non plus puisque nous pouvons réécrire la grammaire ci-dessus pour

for_loop = 'for' '(' declaration · for_loop_rest ')'
for_loop_rest =  · ';' expression ';' expression
for_loop_rest = · 'in' expression

et tous les jetons nécessaires à la décision peuvent être vus sans retour en arrière.

Tenez compte de la convivialité

Java a toujours tendu vers la simplicité sémantique et syntaxique. Par exemple, le langage ne prend pas en charge la surcharge des opérateurs car cela rendrait le code beaucoup plus compliqué. Ainsi, lorsque nous décidons entre inet :pour une syntaxe de boucle pour chaque, nous devons considérer laquelle est moins déroutante et plus apparente pour les utilisateurs. Le cas extrême serait probablement

for (in in in in())
for (in in : in())

(Remarque: Java a des espaces de noms distincts pour les noms de type, les variables et les méthodes. Je pense que c'était surtout une erreur. Cela ne signifie pas que la conception de langage ultérieure doit ajouter plus d' erreurs.)

Quelle alternative offre des séparations visuelles plus claires entre la variable d'itération et la collection itérée? Quelle alternative peut être reconnue plus rapidement lorsque vous regardez le code? J'ai trouvé que séparer les symboles valait mieux qu'une chaîne de mots en ce qui concerne ces critères. D'autres langues ont des valeurs différentes. Par exemple, Python énonce de nombreux opérateurs en anglais afin qu'ils puissent être lus naturellement et sont faciles à comprendre, mais ces mêmes propriétés peuvent rendre assez difficile la compréhension d'un morceau de Python en un coup d'œil.

amon
la source
17

La syntaxe de boucle for-each a été ajoutée en Java 5. Vous devez créer inun mot-clé de langue, et ajouter des mots-clés à une langue plus tard est quelque chose que vous évitez à tout prix car cela casse le code existant - soudain, toutes les variables nommées in provoquent une analyse Erreur. enumétait déjà assez mauvais à cet égard.

Michael Borgwardt
la source
2
Cela semble ... gênant. Cela présuppose que les concepteurs de langage étaient suffisamment bons pour prévoir la plupart des mots clés requis dès le départ. Je ne suis pas sûr que ce soit même nécessaire; des compilateurs décents peuvent déterminer si un mot clé est ou non une variable par son contexte.
Robert Harvey
2
Je ne pense pas que Java ait des mots-clés contextuels comme C #. Donc, utiliser inaurait signifié introduire un nouveau mot-clé, brisant ainsi la compatibilité descendante ( System.in, n'importe qui?) Ou introduire un tout nouveau concept inconnu auparavant (mots-clés contextuels). Tout pour quel gain?
Jörg W Mittag
2
Quel mal des mots-clés contextuels?
user2418306
5
@ user2418306 L'ajout d'un mot clé n'a pas à casser le code existant, à condition que la langue ne soit pas analysée avec une phase de lexer distincte. En particulier, un «in» in for(variable in expression)ne peut jamais être ambigu avec un code juridique, même si «in» peut être utilisé pour des variables. Cependant, une phase de lexer distincte est assez courante dans de nombreuses chaînes d'outils du compilateur. Cela rendrait impossible ou du moins beaucoup plus difficile d'analyser Java avec certains générateurs d'analyseurs courants. Garder la syntaxe d'un langage simple est généralement bon pour toutes les personnes impliquées; tout le monde n'a pas besoin de monstruosités syntaxiques comme C ++ ou Perl.
amon
1
@RobertHarvey: N'oubliez pas cela constet ce gotosont des mots réservés en Java, mais ils ne sont pas (encore) utilisés.
TMN