¿Cuándo se justifica `eval` en Ruby?

” ¿Se supone que ‘eval‘ es desagradable? ” Inspiró este:

En su mayoría, todos están de acuerdo en que eval es malo, y en la mayoría de los casos hay un reemplazo más elegante / seguro.

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

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

Editar: La intención 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 progtwigdor”.

Edit2: Y cuando digo eval , por supuesto me refiero a eval string, no pasando ruby ​​block a instance_eval o class_eval .

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 que está mal. 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 eval de cadena 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.

¿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 (en 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 me parece bastante razonable: una situación en la que específicamente no sabes qué código vas a tener que ejecutar hasta el momento en que te piden que lo hagas. Algo dynamic e interactivo parece encajar en la factura.

La razón por la cual eval está ahí es porque cuando lo necesitas, cuando realmente lo 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.

Yo diría que cuando encuentras un lenguaje de progtwigción sin peligro, has encontrado uno que no es muy útil.

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

Hay un caso de uso muy importante para eval() que no se puede (AFAIK) se puede lograr usando cualquier otra cosa, y eso es encontrar la referencia de objeto correspondiente para un enlace.

Supongamos que se le ha pasado un bloque pero (por algún motivo) necesita acceder al contexto del objeto del enlace, usted haría lo siguiente:

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

También es útil definir lo siguiente:

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

IMO principalmente para lenguajes específicos del dominio.

” Opciones de evaluación en Ruby ” es un artículo de Jay Fields al respecto en InfoQ.

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

Una herramienta como eval se trata de evaluar el código en tiempo de ejecución frente al tiempo de “comstackció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 decente recursivo dependen del lenguaje que se analiza. 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.

” Progtwigr rellenar un letrec en Scheme. Macros o eval? ” Es una pregunta que publiqué sobre eval en Scheme, donde su uso es casi inevitable.

En general, eval es una función 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 ruby instance_eval y class_eval tienen otros usos.