Estoy desarrollando un lenguaje que tengo la intención de reemplazar tanto Javascript como PHP. (No veo ningún problema con esto. No es que ninguno de estos idiomas tenga una gran base de instalación).
Una de las cosas que quería cambiar era convertir el operador de asignación en un comando de asignación, eliminando la capacidad de hacer uso del valor devuelto.
x=1; /* Assignment. */
if (x==1) {} /* Comparison. */
x==1; /* Error or warning, I've not decided yet. */
if (x=1) {} /* Error. */
Sé que esto significaría que esas funciones de una línea que las personas C aman tanto ya no funcionarían. Pensé (con poca evidencia más allá de mi experiencia personal) que la gran mayoría de las veces que esto sucedió, realmente tenía la intención de ser una operación de comparación.
¿O es eso? ¿Hay algún uso práctico del valor de retorno del operador de asignación que no pueda ser reescrito trivialmente? (Para cualquier idioma que tenga dicho concepto).
fuente
while((x = getValue()) != null) {}
. Los reemplazos serán más feos ya que necesitarás usarbreak
o repetir lax = getValue
tarea.Respuestas:
Técnicamente, puede valer la pena conservar un poco de azúcar sintáctico, incluso si se puede reemplazar trivialmente, si mejora la legibilidad de alguna operación común. Pero la asignación como expresión no cae dentro de eso. El peligro de escribirlo en lugar de una comparación significa que rara vez se usa (a veces incluso está prohibido por las guías de estilo) y provoca una doble toma cada vez que se usa. En otras palabras, los beneficios de legibilidad son pequeños en número y magnitud.
Un vistazo a los idiomas existentes que hacen esto puede valer la pena.
if (x)
en lugarif (x != null)
oif (x != 0)
dependiendo del tipo dex
.Sin embargo, Python permite un caso especial, asignando a varios nombres a la vez:
a = b = c
. Esto se considera una declaración equivalente ab = c; a = b
, y se usa ocasionalmente, por lo que puede valer la pena agregarlo a su idioma también (pero no me preocuparía, ya que esta adición debería ser compatible con versiones anteriores).fuente
a = b = c
que las otras respuestas realmente no mencionan.:=
para la asignación.=
es asignación,==
es comparación.if (a = true)
arrojará una advertencia C4706 (The test value in a conditional expression was the result of an assignment.
). GCC con C también arrojará awarning: suggest parentheses around assignment used as truth value [-Wparentheses]
. Esas advertencias pueden silenciarse con un conjunto adicional de paréntesis, pero están ahí para alentar a indicar explícitamente que la asignación fue intencional.a = true
no evaluar a un Booleano y por lo tanto no es un error, sino que también plantea una advertencia relacionada en C #.En general, no. La idea de que el valor de una expresión de asignación sea el valor asignado significa que tenemos una expresión que puede usarse tanto para su efecto secundario como para su valor , y que muchos consideran confusa.
Usos comunes son típicamente para hacer expresiones compactas:
tiene la semántica en C # de "convertir z al tipo de y, asignar el valor convertido a y, el valor convertido es el valor de la expresión, convertir eso al tipo de x, asignar a x".
Pero ya estamos en el ámbito de los efectos secundarios impertativos en un contexto de declaración, por lo que realmente hay muy poco beneficio convincente sobre eso
De manera similar con
ser una taquigrafía para
Nuevamente, en el código original estamos usando una expresión tanto por sus efectos secundarios como por su valor, y estamos haciendo una declaración que tiene dos efectos secundarios en lugar de uno. Ambos son malolientes; trate de tener un efecto secundario por enunciado y use expresiones para sus valores, no para sus efectos secundarios.
Si realmente quiere ser audaz y enfatizar que la asignación es una declaración y no una igualdad, entonces mi consejo es: que sea claramente una declaración de asignación .
El rojo. O
o mejor:
O incluso mejor aún
No hay absolutamente ninguna forma de confundir ninguno de esos
x == 1
.fuente
x[⍋x←6?40]
APL requirió su propio teclado especial, pero fue un lenguaje bastante exitoso.a+b*c --> x
? Esto me parece extraño.Muchos idiomas eligen la ruta para hacer que la asignación sea una declaración en lugar de una expresión, incluido Python:
y Golang:
Otros idiomas no tienen asignación, sino enlaces de ámbito, por ejemplo, OCaml:
Sin embargo,
let
es una expresión en sí misma.La ventaja de permitir la asignación es que podemos verificar directamente el valor de retorno de una función dentro del condicional, por ejemplo, en este fragmento de Perl:
Perl también incluye la declaración a ese condicional solamente, lo que lo hace muy útil. También advertirá si asigna dentro de un condicional sin declarar una nueva variable allí;
if ($foo = $bar)
advertirá,if (my $foo = $bar)
no lo hará.Hacer la asignación en otra declaración suele ser suficiente, pero puede traer problemas de alcance:
Golang depende en gran medida de los valores de retorno para la comprobación de errores. Por lo tanto, permite que un condicional tome una declaración de inicialización:
Otros lenguajes usan un sistema de tipos para no permitir expresiones no booleanas dentro de un condicional:
Por supuesto, eso falla cuando se usa una función que devuelve un valor booleano.
Ahora hemos visto diferentes mecanismos para defenderse contra la asignación accidental:
let
enlacesLos he clasificado en orden de preferencia ascendente: las asignaciones dentro de las expresiones pueden ser útiles (y es simple evitar los problemas de Python al tener una sintaxis de declaración explícita y una sintaxis de argumento con nombre diferente). Pero está bien no permitirlos, ya que hay muchas otras opciones para el mismo efecto.
El código sin errores es más importante que el código conciso.
fuente
Dijiste "Pensé (con poca evidencia más allá de mi experiencia personal) que la gran mayoría de las veces que esto sucedió, realmente tenía la intención de ser una operación de comparación".
¿Por qué no arreglar el problema?
En lugar de = para asignación y == para prueba de igualdad, ¿por qué no usar: = para asignación y = (o incluso ==) para igualdad?
Observar:
Si desea dificultar que el programador confunda la asignación con la igualdad, entonces hágalo más difícil.
Al mismo tiempo, si REALMENTE quisiera solucionar el problema, eliminaría la vasija C que afirmaba que los booleanos solo eran enteros con nombres simbólicos predefinidos de azúcar. Hazlos un tipo completamente diferente. Entonces, en lugar de decir
obligas al programador a escribir:
El hecho es que la asignación como operador es una construcción muy útil. No eliminamos las cuchillas de afeitar porque algunas personas se cortaron. En cambio, el rey Gillette inventó la navaja de seguridad.
fuente
:=
asignación y la=
igualdad podrían solucionar este problema, pero a costa de alienar a todos los programadores que no crecieron utilizando un pequeño conjunto de lenguajes no convencionales. (2) Los tipos que no son permitidos en bools en condiciones no siempre se deben a mezclar bools y enteros, es suficiente para dar una interpretación verdadera / falsa a otros tipos. Un lenguaje más nuevo que no tiene miedo de desviarse de C lo ha hecho para muchos tipos distintos de los enteros (por ejemplo, Python considera falsas las colecciones vacías).if (a = b)
lvalue a, boolean a, b). En un lenguaje sin tipeo estático, también ofrece mensajes de error mucho mejores (en tiempo de análisis vs. tiempo de ejecución). Además, prevenir "a = b vs. a == b errores" puede no ser el único objetivo relevante. Por ejemplo, también me gustaría permitir que el código quisieraif items:
significarif len(items) != 0
, y que tendría que renunciar para restringir las condiciones a los booleanos.Para responder realmente la pregunta, sí, hay numerosos usos de esto, aunque son un poco nicho.
Por ejemplo en Java:
La alternativa sin usar la asignación incrustada requiere lo
ob
definido fuera del alcance del bucle y dos ubicaciones de código separadas que llaman a x.next ().Ya se ha mencionado que puede asignar múltiples variables en un solo paso.
Este tipo de cosas es el uso más común, pero los programadores creativos siempre encontrarán más.
fuente
ob
objeto con cada bucle?Ya que puedes inventar todas las reglas, ¿por qué ahora permitir que la asignación convierta un valor, y simplemente no permitir asignaciones dentro de pasos condicionales? Esto le proporciona el azúcar sintáctico para facilitar las inicializaciones, al tiempo que evita un error de codificación común.
En otras palabras, legalice esto:
Pero haz esto ilegal:
fuente
a = b = c
parece más ortogonal y también más fácil de implementar. Estos dos enfoques no están de acuerdo acerca de la asignación en las expresiones (a + (b = c)
), pero usted no ha tomado partido sobre ellos, así que supongo que no importan.Por lo que parece, estás en el camino de crear un lenguaje bastante estricto.
Con eso en mente, obligar a las personas a escribir:
en lugar de:
puede parecer una mejora para evitar que las personas hagan:
cuando tenían la intención de hacer:
pero al final, este tipo de errores son fáciles de detectar y advertir si son o no códigos legales.
Sin embargo, hay situaciones en las que hacer:
no significa eso
será verdad
Si c es realmente una función c (), entonces podría devolver resultados diferentes cada vez que se llama. (también podría ser computacionalmente costoso también ...)
Del mismo modo, si c es un puntero al hardware mapeado en memoria, entonces
Es probable que ambos sean diferentes, y también pueden tener efectos electrónicos en el hardware en cada lectura.
Hay muchas otras permutaciones con hardware en las que debe ser preciso sobre qué direcciones de memoria se leen, se escriben y bajo restricciones de tiempo específicas, donde hacer múltiples asignaciones en la misma línea es rápido, simple y obvio, sin los riesgos de tiempo que introducir variables temporales
fuente
a = b = c
no esa = c; b = c
, esb = c; a = b
. Esto evita la duplicación de efectos secundarios y también mantiene la modificacióna
yb
en el mismo orden. Además, todos estos argumentos relacionados con el hardware son un poco estúpidos: la mayoría de los idiomas no son lenguajes de sistema y no están diseñados para resolver estos problemas ni se utilizan en situaciones en las que ocurren estos problemas. Esto va doblemente para un lenguaje que intenta desplazar JavaScript y / o PHP.a=b=c
es común / útil, son ejemplos de lugares donde se debe tener en cuenta el orden y la cantidad de efectos secundarios. Eso es completamente independiente. Vuelva a escribir la asignación encadenada correctamente y ambas variantes son igualmente correctas.b
en una temperatura, y eso se convierte al tipo dea
en otra temperatura. El tiempo relativo de cuándo esos valores se almacenan realmente no está especificado. Desde una perspectiva de diseño de lenguaje, creo que es razonable exigir que todos los valores en una declaración de asignación múltiple tengan un tipo coincidente, y posiblemente exigir que ninguno de ellos sea volátil.El mayor beneficio para mi mente de que la asignación sea una expresión es que permite que su gramática sea más simple si uno de sus objetivos es que "todo es una expresión", un objetivo de LISP en particular.
Python no tiene esto; tiene expresiones y declaraciones, la asignación es una declaración. Pero debido a que Python define una
lambda
forma como una única expresión parametrizada , eso significa que no puede asignar variables dentro de una lambda. Esto es inconveniente a veces, pero no es un problema crítico, y es el único inconveniente en mi experiencia para que la asignación sea una declaración en Python.Una forma de permitir que la asignación, o más bien el efecto de la asignación, sea una expresión sin introducir el potencial de
if(x=1)
accidentes que tiene C es usar unalet
construcción similar a LISP , como la(let ((x 2) (y 3)) (+ x y))
que podría evaluar en su lenguaje5
. El uso delet
esta manera no tiene por qué ser una tarea técnica en su idioma, si definelet
como crear un ámbito léxico. Definido de esa manera, unalet
construcción podría compilarse de la misma manera que construir y llamar a una función de cierre anidada con argumentos.Por otro lado, si simplemente está interesado en el
if(x=1)
caso, pero desea que la asignación sea una expresión como en C, tal vez solo sea suficiente elegir diferentes tokens. Asignación:x := 1
ox <- 1
. Comparación:x == 1
. Error de sintaxis:x = 1
.fuente
let
difiere de la asignación en más formas que técnicamente la introducción de una nueva variable en un nuevo alcance. Para empezar, no tiene ningún efecto sobre el código fuera dellet
cuerpo del 's, y por lo tanto requiere anidar todo el código (lo que debería usar la variable) más adelante, una desventaja significativa en el código de asignación pesada. Si uno fuera por esa ruta,set!
sería el mejor análogo de Lisp, completamente diferente a la comparación, pero sin requerir anidamiento o un nuevo alcance.En efecto. Esto no es nada nuevo, todos los subconjuntos seguros del lenguaje C ya han llegado a esta conclusión.
MISRA-C, CERT-C y así sucesivamente todas las asignaciones de prohibición dentro de las condiciones, simplemente porque es peligroso.
No existe ningún caso en el que el código que depende de la asignación dentro de las condiciones no pueda reescribirse.
Además, dichos estándares también advierten contra la escritura de código que se basa en el orden de evaluación. Asignaciones múltiples en una sola fila
x=y=z;
es tal caso. Si una fila con múltiples asignaciones contiene efectos secundarios (funciones de llamada, acceso a variables volátiles, etc.), no puede saber qué efecto secundario ocurrirá primero.No hay puntos de secuencia entre la evaluación de los operandos. Por lo tanto, no podemos saber si la subexpresión
y
se evalúa antes o despuész
: es un comportamiento no especificado en C. Por lo tanto, dicho código es potencialmente poco confiable, no portátil y no conforme con los subconjuntos seguros mencionados de C.La solución hubiera sido reemplazar el código con
y=z; x=y;
. Esto agrega un punto de secuencia y garantiza el orden de evaluación.Entonces, en base a todos los problemas que esto causó en C, cualquier lenguaje moderno haría bien tanto en prohibir la asignación dentro de las condiciones, como en múltiples asignaciones en una sola fila.
fuente