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


"¿Se supone que "eval" es desagradable? " inspiró este:

Casi todo el mundo está 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 tan a menudo, ¿es realmente necesario como una característica del lenguaje? Está haciendo más mal que bien?

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

Editar: La intención de esta pregunta es obtener tantos casos de la vida real como sea posible cuando eval es la única o la mejor solución. Así que, por favor, no entres en la dirección de "si un lenguaje limita la creatividad de un programador".

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

 23
Author: Community, 2009-12-14

8 answers

El único caso que conozco (aparte de "Tengo esta cadena y quiero ejecutarla") es tratar dinámicamente con variables locales y globales. Ruby tiene métodos para obtener los nombres de variables locales y globales, pero carece de métodos para obtener o establecer sus valores basados en estos nombres. La única manera 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 hay otros, pero cada otro caso de uso que he visto donde 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.

 21
Author: Chuck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-14 19:45:04

¿Cuándo se justifica? Yo diría que cuando no hay 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 me parece bastante razonable, una situación en la que específicamente no sabes qué código vas a tener que ejecutar hasta el mismo momento en que se te pide que lo hagas. Algo dinámico y interactive parece encajar en el proyecto de ley.

 12
Author: Mike Woodhouse,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-14 19:59:31

La razón por la que 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 necesitas ejecutar código arbitrario.

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

Yo diría que cuando encuentras una programación lenguaje desprovisto de peligro, has encontrado uno que no es muy útil.

¿Cuándo se justifica la evaluación? En términos pragmáticos, cuando dices que lo es. Si es su programa y usted es el programador, usted establece los parámetros.

 7
Author: tadman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-14 18:58:01

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

Digamos que se le ha pasado un bloque pero (por alguna razón) necesita acceso al contexto del objeto del enlace, 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
 5
Author: horseyguy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-15 09:47:05

IMO principalmente para Idiomas Específicos de Dominio.

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

 4
Author: Diego Dias,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-13 16:23:12

La evaluación es una herramienta, no es ni inherentemente buena ni mala. Se justifica siempre que esté seguro de que es la herramienta correcta para lo que está tratando de lograr.

 2
Author: Bryan Oakley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-14 19:04:04

Una herramienta como eval se trata de evaluar código en tiempo de ejecución vs.tiempo de "compilación". ¿Sabes cuál es el código cuando lanzas Ruby? Entonces probablemente no necesites evaluación. ¿Su 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 recursivo decente dependen del lenguaje que se está analizando. Si su aplicación construye un analizador sobre la marcha, entonces podría tener sentido usar eval. Usted podría escribir un analizador generalizado, pero puede que no sea una solución tan elegante.

"Rellenando programáticamente un letrec en el Esquema. ¿Macros o evaluación?" es una pregunta que publiqué sobre eval en Scheme, donde su uso es principalmente inevitable.

 1
Author: z5h,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:20

En general, eval es una función de lenguaje útil cuando desea ejecutar código arbitrario. Esto debería ser algo raro, pero tal vez esté haciendo su propia 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 característica existe. Si lo está utilizando para trabajar alrededor de alguna parte del lenguaje (por ejemplo, variables globales), entonces el lenguaje es defectuoso o su comprensión del lenguaje es defectuosa. La solución es normalmente no utilizar eval pero para entender mejor el idioma o elegir un idioma diferente.

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

 1
Author: olleicua,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-26 17:52:10