¿Por qué en casi todos los lenguajes de programación modernos (Go, Rust, Kotlin, Swift, Scala, Nim, incluso la última versión de Python) los tipos siempre vienen después del nombre de la variable en la declaración de la variable, y no antes?
¿Por qué x: int = 42
y no int x = 42
?
¿No es este último más legible que el primero?
¿Es solo una tendencia o hay alguna razón realmente significativa detrás de esta solución?
programming-languages
language-design
syntax
Andre Polykanine
fuente
fuente
Respuestas:
Todos los idiomas que mencionó admiten la inferencia de tipos , lo que significa que el tipo es una parte opcional de la declaración en esos idiomas porque son lo suficientemente inteligentes como para completarlos cuando proporciona una expresión de inicialización que tiene un tipo fácil de determinar.
Eso es importante porque poner las partes opcionales de una expresión más a la derecha reduce la ambigüedad de análisis y aumenta la coherencia entre las expresiones que usan esa parte y las que no. Es más simple analizar una declaración cuando sabes que la
var
palabra clave y el nombre de la variable son obligatorios antes de llegar a las cosas opcionales. En teoría, todas esas cosas que facilitan el análisis de las computadoras también deberían mejorar la legibilidad general para los humanos, pero eso es mucho más discutible.Este argumento se pone especialmente fuerte cuando se tiene en cuenta todos los modificadores de tipo opcional que un lenguaje "no modernos" como C ++ tiene, como
*
para los punteros,&
para las referencias,const
,volatile
y así sucesivamente. Una vez que agrega comas para varias declaraciones, comienza a obtener algunas ambigüedades realmente extrañas, comoint* a, b;
no hacerb
un puntero.Incluso C ++ ahora admite declaraciones de "tipo a la derecha" en forma de
auto x = int{ 4 };
, y tiene algunas ventajas .fuente
var
esa proporcionan la mayoría de la simplificación del análisis.var
palabra clave no vence el propósito de la notación de primer nombre? No veo la ventaja de escribirvar userInput: String = getUserInput()
sobreString userInput = getUserInput()
. La ventaja solo sería relevante siuserInput = getUserInput()
también se permite, pero eso implicaría la declaración implícita de una variable de ámbito, lo que me parece bastante aborrecible.var userInput = getUserInput()
oauto userInput = getUserInput()
, el compilador sabegetUserInput()
retornosString
para que no tenga que especificarlo con la palabra clave de deducción de tipo.userInput = getUserInput()
es, por supuesto, usar antes de declarar.Su premisa es defectuosa en dos frentes:
Pascal, ML, CLU, Modula-2 y Miranda fueron idiomas muy influyentes, por lo que no es sorprendente que este estilo de declaraciones tipográficas siguiera siendo popular.
La legibilidad es una cuestión de familiaridad. Personalmente, encuentro que los chinos son ilegibles, pero aparentemente, los chinos no lo hacen. Después de haber aprendido a Pascal en la escuela, incursionar en Eiffel, F Has, Haskell y Scala, y haber examinado TypeScript, Fortress, Go, Rust, Kotlin, Idris, Frege, Agda, ML, Ocaml, ... me parece completamente natural. .
Si es una tendencia, es bastante persistente: como mencioné, se remonta a cien años en matemáticas.
Una de las principales ventajas de tener el tipo después del identificador, es que es fácil omitir el tipo si desea inferirlo. Si sus declaraciones se ven así:
Entonces es trivial dejar de lado el tipo e inferirlo así:
Mientras que si el tipo viene antes del identificador como este:
Entonces comienza a ser difícil para el analizador distinguir una expresión de una declaración:
La solución que los diseñadores de idiomas suelen presentar es introducir una palabra clave "No quiero escribir un tipo" que debe escribirse en lugar del tipo:
Pero esto en realidad no tiene mucho sentido: básicamente tienes que escribir explícitamente un tipo que dice que no escribes un tipo. ¿Eh? Sería mucho más fácil y más sensato dejarlo fuera, pero eso hace que la gramática sea mucho más compleja.
(Y ni siquiera hablemos sobre los tipos de puntero de función en C.)
Los diseñadores de varios de los idiomas mencionados anteriormente han intervenido en este tema:
Preguntas frecuentes sobre Go (consulte también: Sintaxis de declaración de Go ):
Preguntas frecuentes sobre Kotlin :
Programación en Scala :
Nota: los diseñadores de Ceilán también documentaron por qué usan la sintaxis de tipo de prefijo :
Personalmente, encuentro su "argumento" mucho menos convincente que los otros.
fuente
var
,auto
,let
, o similares, y no por qué lado de la variable de la declaración de tipo está encendido.var Int x = 5
o inclusoInt var x = 5
podría ser acortado a ambosvar x = 5
."Your premise is flawed on two fronts"
Todos estos son más nuevos que C # o D.func
en Go.Pascal también lo hace, y no es un idioma nuevo. Sin embargo, era un lenguaje académico, diseñado desde cero.
Diría que es semánticamente más claro comenzar con el nombre de la variable. El tipo es solo un detalle técnico. Si desea leer su clase como el modelo de realidad que es, tiene sentido poner los nombres de sus entidades primero y su implementación técnica al final.
C # y Java provienen de C, por lo que tuvieron que respetar la "técnica anterior", para no confundir al programador experimentado.
fuente
En un ejemplo tan simple, no hay mucha diferencia, pero hagámoslo un poco más complejo:
int* a, b;
Esta es una declaración real y válida en C, pero no hace lo que intuitivamente parece que debería hacer. Parece que estamos declarando dos variables de tipo
int*
, pero en realidad estamos declarando unaint*
y unaint
.Si su idioma coloca el tipo después de los nombres de las variables en sus declaraciones, es imposible encontrar ese problema.
fuente
x, y *int;
dos*int
o uno*int
y unoint
? Hacerint*
o*int
una ficha en lugar de dos lo resolvería, o usar un elemento sintáctico adicional como el:
, que podría funcionar en cualquier dirección.:
oas
, entre el nombre y el tipo.x, y int
sin separador.x, y *int
significa "declarar dos variables, x e y, con el tipo puntero a int".int[] x, y
como dos matrices. Es solo la sintaxis C tonta que trata esos modificadores como operadores unarios en la variable (y una mezcla de prefijo y postfix, nada menos) que causa problemas.