¿Por qué exactamente eval es malo?
Sé que los programadores de Lisp y Scheme suelen decir que eso eval
debería evitarse a menos que sea estrictamente necesario. He visto la misma recomendación para varios lenguajes de programación, pero aún no he visto una lista de argumentos claros en contra del uso de eval
. ¿ Dónde puedo encontrar una descripción de los posibles problemas de uso eval
?
Por ejemplo, conozco los problemas de GOTO
la programación procedimental (hace que los programas sean ilegibles y difíciles de mantener, hace que los problemas de seguridad sean difíciles de encontrar, etc.), pero nunca he visto los argumentos en contra eval
.
Curiosamente, los mismos argumentos en contra GOTO
deberían ser válidos contra las continuaciones, pero veo que los intrigantes, por ejemplo, no dirán que las continuaciones sean "malas"; solo debes tener cuidado al usarlas. Es mucho más probable que desaprueben el uso de código eval
que el uso de continuaciones (hasta donde puedo ver, podría estar equivocado).
Hay varias razones por las que no se debe utilizar EVAL
.
La razón principal para los principiantes es: no lo necesitas.
Ejemplo (asumiendo Common Lisp):
EVALUAR una expresión con diferentes operadores:
(let ((ops '(+ *)))
(dolist (op ops)
(print (eval (list op 1 2 3)))))
Eso está mejor escrito como:
(let ((ops '(+ *)))
(dolist (op ops)
(print (funcall op 1 2 3))))
Hay muchos ejemplos en los que los principiantes que aprenden Lisp creen que necesitan EVAL
, pero no lo necesitan, ya que las expresiones se evalúan y también se puede evaluar la parte de la función. La mayoría de las veces el uso de EVAL
muestra una falta de comprensión del evaluador.
Es el mismo problema con las macros. A menudo, los principiantes escriben macros, donde deberían escribir funciones, sin comprender para qué sirven realmente las macros y sin comprender que una función ya hace el trabajo.
A menudo es la herramienta incorrecta para el trabajo EVAL
y a menudo indica que el principiante no comprende las reglas habituales de evaluación de Lisp.
Si cree que lo necesita EVAL
, compruebe si se puede utilizar algo como o en su lugar FUNCALL
.REDUCE
APPLY
FUNCALL
- llamar a una función con argumentos:(funcall '+ 1 2 3)
REDUCE
- llamar a una función en una lista de valores y combinar los resultados:(reduce '+ '(1 2 3))
APPLY
- llamar a una función con una lista como argumentos:(apply '+ '(1 2 3))
.
P: ¿Realmente necesito eval o el compilador/evaluador ya es lo que realmente quiero?
Las principales razones a evitar EVAL
para usuarios un poco más avanzados:
desea asegurarse de que su código esté compilado, porque el compilador puede verificar el código en busca de muchos problemas y genera código más rápido, a veces MUCHO MUCHO (eso es factor 1000 ;-)) código más rápido
El código que se construye y necesita ser evaluado no se puede compilar lo antes posible.
La evaluación de entradas arbitrarias del usuario abre problemas de seguridad.
Algún uso de la evaluación
EVAL
puede ocurrir en el momento equivocado y crear problemas de compilación.
Para explicar el último punto con un ejemplo simplificado:
(defmacro foo (a b)
(list (if (eql a 3) 'sin 'cos) b))
Por lo tanto, es posible que desee escribir una macro que, según el primer parámetro, utilice SIN
o COS
.
(foo 3 4)
hace (sin 4)
y (foo 1 4)
hace (cos 4)
.
Ahora podemos tener:
(foo (+ 2 1) 4)
Esto no da el resultado deseado.
Entonces es posible que desee reparar la macro FOO
EVAluando la variable:
(defmacro foo (a b)
(list (if (eql (eval a) 3) 'sin 'cos) b))
(foo (+ 2 1) 4)
Pero esto todavía no funciona:
(defun bar (a b)
(foo a b))
El valor de la variable simplemente no se conoce en el momento de la compilación.
Una razón general importante para evitarlo EVAL
: a menudo se usa para trucos feos.
eval
(en cualquier idioma) no es malo de la misma manera que una motosierra no es mala. Es una herramienta. Resulta ser una herramienta poderosa que, cuando se usa mal, puede cortar extremidades y destripar (metafóricamente hablando), pero lo mismo puede decirse de muchas herramientas en la caja de herramientas de un programador, incluidas:
goto
y amigos- subprocesamiento basado en bloqueo
- continuaciones
- macros (higiénicas u otras)
- punteros
- excepciones reiniciables
- código automodificable
- ...y un elenco de miles.
Si tiene que utilizar alguna de estas herramientas poderosas y potencialmente peligrosas, pregúntese tres veces "¿por qué?" en una cadena. Por ejemplo:
"¿Por qué tengo que usar
eval
?" "Por foo". "¿Por qué es necesario foo?" "Porque ..."
Si llega al final de esa cadena y la herramienta todavía parece ser lo correcto, entonces hágalo. Documente muchísimo. Pruébalo a fondo. Verifique la corrección y la seguridad una y otra vez. Pero hazlo.
Eval está bien, siempre y cuando sepas EXACTAMENTE lo que implica. Cualquier entrada del usuario DEBE ser verificada y validada y todo. Si no sabes cómo estar 100% seguro, no lo hagas.
Básicamente, un usuario puede escribir cualquier código para el idioma en cuestión y se ejecutará. Puedes imaginarte cuánto daño puede causar.
"¿Cuándo debo usar eval
?" podría ser una mejor pregunta.
La respuesta corta es "cuando su programa está destinado a escribir otro programa en tiempo de ejecución y luego ejecutarlo". La programación genética es un ejemplo de una situación en la que probablemente tenga sentido utilizarla eval
.