De la guía de lenguaje Java 5 :
Cuando vea los dos puntos (:) léalo como "en".
¿Por qué no usar in
en primer lugar entonces?
Esto me ha estado molestando durante años. Porque es inconsistente con el resto del lenguaje. Por ejemplo, en Java hay implements
, extends
, super
para las relaciones entre los tipos en lugar de símbolos como en C ++, Scala o Ruby.
En Java dos puntos utilizados en 5 contextos . Tres de los cuales son heredados de C. Y otros dos fueron respaldados por Joshua Bloch. Al menos, eso fue lo que dijo durante la charla "La controversia de los cierres" . Esto surge cuando critica el uso de dos puntos para el mapeo como inconsistente con la semántica para cada uno. Lo que me parece extraño porque son los patrones esperados abusados para cada uno. Me gusta list_name/category: elements
o laberl/term: meaning
.
Estuve husmeando en jcp y jsr, pero no encontré ninguna señal de la lista de correo. Google no encontró discusiones sobre este asunto. Solo los novatos confundidos por el significado de colon en for
.
Principales argumentos en contra in
provistos hasta ahora:
- requiere nueva palabra clave; y
- complica el lexing
Veamos definiciones gramaticales relevantes :
declaración : instrucción 'for' '(' forControl ')' El | ... ; forControl : EnhancedForControl El | forInit? ';' ¿expresión? ';' forUpdate? ; EnhancedForControl : variableModificador * tipo variableDeclaratorId ':' expresión ;
Cambiar de :
a in
no trae complejidad adicional o requiere una nueva palabra clave
Respuestas:
Los analizadores normales, como generalmente se enseñan, tienen una etapa lexer antes de que el analizador toque la entrada. El lexer (también "escáner" o "tokenizador") corta la entrada en pequeños tokens que están anotados con un tipo. Esto permite que el analizador principal use tokens como elementos terminales en lugar de tener que tratar a cada personaje como un terminal, lo que conduce a ganancias de eficiencia notables. En particular, el lexer también puede eliminar todos los comentarios y espacios en blanco. Sin embargo, una fase de tokenizador separada significa que las palabras clave no se pueden usar también como identificadores (a menos que el idioma admita la eliminación de caracteres que ha caído en desuso o prefija todos los identificadores con un sigilo
$foo
).¿Por qué? Supongamos que tenemos un tokenizador simple que comprende los siguientes tokens:
El tokenizer siempre coincidirá con el token más largo y preferirá las palabras clave sobre los identificadores. Entonces
interesting
será lexed comoIDENT:interesting
, peroin
será lexed comoIN
, nunca comoIDENT:interesting
. Un fragmento de código comoserá traducido a la secuencia de tokens
Hasta ahora, eso funciona. Pero cualquier variable
in
sería lexed como la palabra clave enIN
lugar de una variable, lo que rompería el código. El lexer no mantiene ningún estado entre los tokens, y no puede saber quein
normalmente debería ser una variable, excepto cuando estamos en un bucle for. Además, el siguiente código debe ser legal:El primero
in
sería un identificador, el segundo sería una palabra clave.Hay dos reacciones a este problema:
Las palabras clave contextuales son confusas, reutilicemos las palabras clave en su lugar.
Java tiene muchas palabras reservadas, algunas de las cuales no tienen uso, excepto para proporcionar mensajes de error más útiles a los programadores que cambian a Java desde C ++. Agregar nuevas palabras clave rompe el código. Agregar palabras clave contextuales es confuso para un lector del código a menos que tenga un buen resaltado de sintaxis, y hace que las herramientas sean difíciles de implementar porque tendrán que usar técnicas de análisis más avanzadas (ver más abajo).
Cuando queremos extender el lenguaje, el único enfoque sensato es usar símbolos que anteriormente no eran legales en el idioma. En particular, estos no pueden ser identificadores. Con la sintaxis del bucle foreach, Java reutilizó la
:
palabra clave existente con un nuevo significado. Con lambdas, Java agregó una->
palabra clave que no podía aparecer previamente en ningún programa legal (-->
aún estaría lex como lo'--' '>'
que es legal, y->
podría haber sido previamente lexed as'-', '>'
, pero esa secuencia sería rechazada por el analizador).Las palabras clave contextuales simplifican los idiomas, impleméntelos
Los Lexers son indiscutiblemente útiles. Pero en lugar de ejecutar un lexer antes del analizador, podemos ejecutarlos en conjunto con el analizador. Los analizadores ascendentes siempre conocen el conjunto de tipos de tokens que serían aceptables en cualquier ubicación. El analizador puede solicitar al lexer que coincida con cualquiera de estos tipos en la posición actual. En un ciclo for-each, el analizador estaría en la posición indicada
·
en la gramática (simplificada) después de que se haya encontrado la variable:En esa posición, los tokens legales son
SEMICOLON
oIN
, pero noIDENT
. Una palabra clavein
sería completamente inequívoca.En este ejemplo en particular, los analizadores de arriba hacia abajo tampoco tendrían un problema, ya que podemos reescribir la gramática anterior para
y todos los tokens necesarios para la decisión se pueden ver sin retroceder.
Considerar usabilidad
Java siempre ha tendido a la simplicidad semántica y sintáctica. Por ejemplo, el lenguaje no admite la sobrecarga del operador porque haría el código mucho más complicado. Entonces, al decidir entre
in
y:
para una sintaxis de bucle for-each, tenemos que considerar cuál es menos confuso y más evidente para los usuarios. El caso extremo probablemente sería(Nota: Java tiene espacios de nombres separados para nombres de tipos, variables y métodos. Creo que esto fue un error, principalmente. Esto no significa que el diseño del lenguaje posterior tenga que agregar más errores).
¿Qué alternativa proporciona separaciones visuales más claras entre la variable de iteración y la colección iterada? ¿Qué alternativa se puede reconocer más rápidamente cuando echas un vistazo al código? Descubrí que los símbolos de separación son mejores que una cadena de palabras cuando se trata de estos criterios. Otros idiomas tienen valores diferentes. Por ejemplo, Python deletrea muchos operadores en inglés para que se puedan leer de forma natural y sean fáciles de entender, pero esas mismas propiedades pueden dificultar la comprensión de una pieza de Python de un vistazo.
fuente
La sintaxis de bucle for-each se agregó en Java 5. Tendría que hacer
in
una palabra clave de idioma, y agregar palabras clave a un idioma más adelante es algo que evita a toda costa porque rompe el código existente; de repente, todas las variables nombradasin
causan un análisis error.enum
fue lo suficientemente malo en ese sentido.fuente
in
hubiera significado introducir una nueva palabra clave, rompiendo así la compatibilidad con versiones anteriores (System.in
¿alguien?) O introduciendo un concepto nuevo y desconocido (palabras clave contextuales). ¿Todo para qué ganancia?for(variable in expression)
nunca puede ser ambiguo con ningún código legal, incluso si "in" puede usarse para variables. Sin embargo, una fase lexer separada es bastante común en muchas cadenas de herramientas de compilación. Esto haría imposible o al menos mucho más difícil analizar Java con algunos generadores de analizadores comunes. Mantener la sintaxis de un lenguaje simple suele ser bueno para todos los involucrados; No todos necesitan monstruosidades sintácticas como C ++ o Perl.const
ygoto
ambas son palabras reservadas en Java, pero aún no se usan.