2009-12-14 8 views
22

"Is 'eval' supposed to be nasty?" inspiró éste:¿Cuándo se justifica `eval` en Ruby?

Parcialmente todo el mundo está de acuerdo en que eval es malo, y en la mayoría de los casos no es más elegante reemplazo/más seguro.

Así que quería preguntar: si eval se utiliza tan a menudo, ¿es realmente necesario como función de idioma? ¿Está haciendo más mal que bien?

Personalmente, el único lugar en el que me resulta útil es para interpolar cadenas proporcionadas en el archivo de configuración.

Editar: El propósito de esta pregunta es obtener tantos casos reales como sea posible cuando eval es la única o la mejor solución. Por lo tanto, por favor, no entre en la dirección "debe un idioma limitar la creatividad de un programador".

Edit2: Y cuando digo eval, por supuesto me refiero a eval ing cadena, no pasa bloque de rubí a instance_eval o class_eval.

Respuesta

20

El único caso que conozco (aparte de "Tengo esta cadena y quiero ejecutarlo") trata dinámicamente las variables locales y globales. Ruby tiene métodos para obtener los nombres de las variables locales y globales, pero carece de métodos para obtener o establecer sus valores en función de estos nombres. La única forma de hacer AFAIK es con eval.

Cualquier otro uso es casi seguro incorrecto.No soy un gurú y no puedo afirmar categóricamente que no haya otros, pero en todos los demás casos de uso que he visto en los que alguien dijo "Necesitas evaluación para esto", he encontrado una solución que no lo hizo.

Tenga en cuenta que estoy hablando de cadena eval aquí, por cierto. Ruby también tiene instance_eval, que puede tomar una cadena o un bloque para ejecutar en el contexto del receptor. La forma de bloque de este método es rápida, segura y muy útil.

+2

Sé que esto va más allá del alcance de la pregunta principal, pero tengo curiosidad por saber en qué casos de uso real se deben asignar dinámicamente variables locales/globales, es decir, cuando se utilizan atributos de instancia y métodos apropiados, instance_variable_set y instance_variable_get no funcionan ? –

+0

Bueno, no siempre quiere almacenar cosas más allá de la invocación al método, y algunas veces quiere usar variables locales para determinar a qué ivars se le asigna. Un lugar donde esto puede ser útil es eliminar la repetición. A menudo, un inicializador tomará argumentos y los asignará a variables de instancia con nombre idéntico. Con 'eval', podría abreviar esto a algo así como' set_ivars (binding) '. – Chuck

+0

este modismo es útil, y solo es posible con eval: 'eval ('self', block.binding)' – horseyguy

7

La razón por la que eval está es porque cuando la necesitas, cuando realmente la necesitas, no hay sustitutos. Después de todo, hay mucho que puedes hacer con el envío de métodos creativos, y en algún momento debes ejecutar código arbitrario.

El hecho de que un idioma tenga una característica que pueda ser peligrosa no significa que sea intrínsecamente malo. Cuando un idioma presume saber más que su usuario, es cuando hay problemas.

Argumentaría que cuando encuentras un lenguaje de programación sin peligro, encuentras uno que no es muy útil.

¿Cuándo se justifica eval? En términos pragmáticos, cuando dices que es. Si es su programa y usted es el programador, configure los parámetros.

+3

Lo único que podría agregar es "está justificado cuando dices que es Y conoces las ramificaciones". –

+0

Mi intención era obtener el mayor número posible de usos justificados de la evaluación, no discutir si los autores de Ruby hicieron lo correcto. Pero si quieres ir allí, diría que a menudo no soy el único que establece los parámetros, como dices, ya que a menudo no soy el único programador del proyecto. Y eval es una de las características del lenguaje más peligroso, caído en manos equivocadas. –

+1

-1. Las generalizaciones y devolver el balón a la corte de MLaden no ayudan a nadie. – z5h

4

IMO principalmente para idiomas específicos del dominio.

"Evaluation Options in Ruby" es un artículo de Jay Fields al respecto en InfoQ.

2

eval es una herramienta, no es intrínsecamente buena ni mala. Está justificado siempre que esté seguro de que es la herramienta adecuada para lo que está tratando de lograr.

+0

Estoy de acuerdo en su mayoría. El problema es cuando los que abusan del eval-hammer comienzan a romper cosas simplemente porque no conocen el idioma; como, ellos reescriben el RTL o algo así. –

+0

@The Wicked Flea: es por eso que incluí "cada vez que estás seguro".Si crees que es la herramienta correcta, no lo es. Solo cuando sepas que es la herramienta correcta y conoces las ramificaciones de esa decisión, de hecho es la herramienta correcta. –

+0

Bueno, estar seguro no justifica automáticamente sus decisiones. Además, argumentaría que algunas herramientas se utilizan con fines benignos con más frecuencia que otras (me vienen a la mente un arado y una bomba). Je, un comentario político para una respuesta política. :( –

1

Una herramienta como eval se trata de evaluar el código en tiempo de ejecución frente al tiempo de "compilación". ¿Sabes qué es el código cuando ejecutas Ruby? Entonces probablemente no necesites eval. ¿Tu código está generando código durante el tiempo de ejecución? entonces probablemente necesites evaluarlo.

Por ejemplo, los métodos/funciones necesarios en un analizador sintáctico decente recursivo dependen del lenguaje que se está analizando. Si su aplicación crea dicho analizador sobre la marcha, entonces podría tener sentido usar eval. Podría escribir un analizador generalizado, pero podría no ser una solución tan elegante.

"Programatically filling in a letrec in Scheme. Macros or eval?" es una pregunta que publiqué sobre eval en Scheme, donde su uso es casi inevitable.

+2

Puede generar funciones en tiempo de ejecución sin usar eval – Chuck

+0

Escribir código que genera un conjunto de funciones mutuamente recursivas sin eval. Luego con eval. Mi opinión es que el segundo ejercicio generalmente será más simple. – z5h

+0

En Ruby, la forma de generar métodos con y sin 'eval' es casi lo mismo, entonces no estoy seguro de por qué esto sería diferente? – henrikhodne

12

¿Cuándo está justificado? Diría cuando no hay una alternativa razonable. Pude pensar en un uso donde no puedo pensar en una alternativa: irb, que si profundizas lo suficiente (a workspace.rb, alrededor de la línea 80 en mi copia si estás interesado) usa eval para ejecutar tu entrada:

def evaluate(context, statements, file = __FILE__, line = __LINE__) 
    eval(statements, @binding, file, line) 
end 

eso parece bastante razonable para mí - una situación en la que específicamente no sabe qué código que va a tener para ejecutar hasta el momento en que se le pide que lo haga. Algo dinámico e interactivo parece encajar en la factura.

5

Hay un caso de uso muy importante para eval() que no se puede (AFAIK) se puede lograr utilizando cualquier otra cosa, y que es encontrar la referencia de objeto correspondiente para una vinculación.

Digamos que se han transmitido un bloque, pero (por alguna razón) necesita tener acceso a objeto de contexto de la unión, tendría que hacer lo siguiente:

obj = eval('self', block.binding) 

También es útil para definir lo siguiente:

class Proc 
    def __context__ 
     eval('self', self.binding) 
    end 
end 
1

En general, eval es una característica de idioma útil cuando desea ejecutar código arbitrario. Esto debería ser algo raro, pero tal vez esté haciendo su propio REPL o quiera exponer el tiempo de ejecución de ruby ​​al usuario final por alguna razón. Podría suceder y es por eso que la función existe. Si lo está utilizando para trabajar en alguna parte del idioma (por ejemplo, variables globales), entonces el idioma es defectuoso o su comprensión del idioma es defectuosa. La solución generalmente no es usar eval, sino para comprender mejor el idioma o elegir un idioma diferente.

Vale la pena señalar que en particular rubí instance_eval y class_eval tienen otros usos.