
Eval() Isn't Quite Pure Evil - fogus
http://blog.grayproductions.net/articles/eval_isnt_quite_pure_evil
======
raganwald
I agree with the objection mentioned in the OP's comments: eval() as described
turns code into strings. As Alan Perlis said:

"The string is a stark data structure and everywhere it is passed there is
much duplication of process. It is a perfect vehicle for hiding information."

(You're tired of the canonical link, so here's a much more fun one:
<http://www.emacswiki.org/emacs/perlisisms.el>)

Strings hide information, and string representations of programming languages
hide the most important things about programs (with the _possible_ exception
of programming languages designed to manipulate strings, like SNOBOL).

OTOH I like using ParseTree to convert Ruby programs into s-expressions and to
Ruby2Ruby convert those s-expressions into strings before evaluating them.
It's overkill at the moment, but somehow it seems to point towards something
much, much better than strings:

    
    
        %w[B C D].each do |name|
          eval(
            Ruby2Ruby.new.process(
              s(:module, 
                name.to_sym, 
                s(:scope, 
                  s(:defn, 
                    :call, 
                    s(:args), 
                    s(:scope, 
                      s(:block, 
                        s(:call, 
                          nil, 
                          :puts, 
                          s(:arglist, s(:str, name))
                        ), 
                        s(:zsuper))
                      )
                  )
                )
              )
            )
          )
        end
    

It's ugly, but at least it _tries_ to represent the structure of the Ruby
code.

~~~
leif
That is so far from anything I would ever want to see on a slide (unless it's
just meant to poke fun at such code), I don't even know where to begin.

When you program, do you think of it as an exercise in bending your mind to
the whim of the computer, or the other way around? I, for one, would rather
make the computer understand what I'm thinking.

If I'm thinking "hey, I want to evaluate this bit a bunch of times with
different names", why can't I just say that? Isn't Ruby supposed to be the
"English With Some Bits Of Punctuation" language? Assuming I've met any
security concerns, and I'm not doing anything incredibly complicated (which,
honestly, wouldn't pass code review with or without eval), what's wrong with
writing the code the way I want?

Looking at the author's received "fix", I don't understand it. I'm not a big
fancy Ruby expert, but I've been through the poignant guide, I've written some
medium-sized stuff with it, I've used most of the syntax constructs, and I
think I get the idioms. The original makes sense; it's clear to me. We do
three string format operations, and eval each one, thus defining a module each
time. Great.

The "fix" confuses me: "what's Module.new (I thought you just did 'module
Name; stuff; end')?", "what's define_method (it looks like def, but why
doesn't it just say def)?", "what's Object.const_set, and why don't I see
'name' until then?". With some reasonable inference, these aren't tough
questions, but there remains one: "what the hell is the control flow diagram
for this?". There are three anonymous functions defined in here, and I have no
idea when the inner ones execute, or worse, are defined or compiled. I thought
I understood Ruby's control flow, and I could make a guess at this, but
there's no way I'd bet money on the result.

So what if eval turns code into strings? Code _is_ strings! Do you think files
are made of parse trees? eval doesn't hide information, it adds another level
of abstraction (and there's an old saying about solving problems in computer
science somewhere in there). Syntax highlighters, test coverage reports,
static analyzers, and cyclomatic complexity calculators (really?) should all
be able to handle it. Why? Because the feature is there, and it makes my life
easier sometimes, and in those times, I want to use it.

So, umm, there! That's what I think about eval.

~~~
raganwald
"Code is strings" in exactly the same sense that "Speech is vibration of
molecules."

The problem with eval-ing strings is that it doesn't _scale_. It's great for
building three identical modules "B," "C," and "D," but the moment you want to
do something interesting, it breaks down. The complexity of manipulating
programs as strings grows far more rapidly than the complexity of things you
want to do.

For example, String#to_proc adds some syntactic sugar to Ruby like implied
parameters so you can write things like: some_array.map(&'+ 1'):

[http://github.com/raganwald/homoiconic/blob/master/2008-11-2...](http://github.com/raganwald/homoiconic/blob/master/2008-11-28/you_cant_be_serious.md#readme)

The implementation is hideous:

[http://github.com/raganwald/homoiconic/blob/master/2008-11-2...](http://github.com/raganwald/homoiconic/blob/master/2008-11-28/string_to_proc.rb)

That's because it's trying to manipulate and eval strings that represent
program syntax where it should be parsing the strings and manipulating data
structures that represent program structure.

~~~
leif
I'm not saying eval does scale, I'm just saying it's not evil. Of course, if I
want to actually manipulate the parse tree, I should parse the string first.
But if I don't need that kind of power, there's no sense in contorting my code
to fit into the awkward metaprogramming constraints of <language>.

~~~
raganwald
I (also) never said it was evil, I said it doesn't scale. But that being said,
I have seen many tools start out being used just as you say, benignly, in
places where they offer convenience for simple cases. Open classes a/k/a
monkey patching falls in this category for me: a tool that works well for
certain straightforward cases. However, programmers seem to have a reach that
exceeds their grasp of the limitations of their tools, and over time people
push the tools right out of their sweet spot and into trouble.

That's not the tool or technique's fault, of course. If people want to amend
core classes with hundreds of kitchen-sink convenience methods or eval much
more complex string manipulations... You might easily say that is the fault of
the programmer for making a poor choice.

------
calcnerd256
The problem with eval is the principle of least power. He almost figured it
out when he said it was too much tool for the job, but then he went on to
useless metrics.

~~~
lucumo
As far as I can tell, the principle of least power[1] applies to descriptive
languages (HTML, RDF, etc.) that are meant to store information. A program by
comparison is meant to store instructions. I'm unsure about the argument
you're making. Can you elaborate a bit?

[1] For those that had to look it up like I had to:
<http://en.wikipedia.org/wiki/Principle_of_Least_Power>

~~~
calcnerd256
I recommend treating it as a spectrum instead of boolean. Eval is considered
harmful for a similar reason as to why GOTO is: it makes code harder to prove
and to manipulate. Markup and data on one end of the spectrum can be endlessly
manipulated, analyzed, and worked with. As we move up the power spectrum, we
get to regular languages, stack languages, and finally Turing-completeness.
Within Turing-completeness, though, there are still varying levels of how hard
something is to work with. Yes, the halting problem means that you can't for
any behavior determine whether an arbitrary program exhibits that behavior,
but there are still subsets about which you can reason.

------
tetha
I'd figure that eval is in the same class as goto and for example Pythons vast
dynamicness and metaprogramming abilities. Let's call this class the class of
extremely powerful programming constructs.

A GOOD programmer can use such an extremely powerful programming construct and
write good, readable code with it which just makes you go 'wow, this is ...
just clean, and I get what it does in a second', which is nicer than any code
one might produce without using these tools, even though everyone knows that
this class of control constructs can create a horrible mess.

However, a BAD programmer can take this class of extremely powerful
programming constructs, and create a horrible mess which requires more time to
understand than a rewrite would take (more than once, probably).

And now, the third fact you need to add into the mix is: Bad programmers
usually don't think they are bad programmers. The realization 'I am actually a
pretty bad programmer' is one major step on the path to being a good
programmer, as absurd as it sounds. Thus, a good programmer will usually not
use this class of extremely power programming constructs, because he knows
that he will probably produce a mess with them (and probably miss some really
nice solutions), while a bad programmer will probably use them happily,
thinking he produces clean and nice code (but he is not).

And this is the reason why gotos, evals and mind-bending meta-programming is
not always bad, and not always evil, but we must not admit this, because
otherwise, everyone will start using it :)

(yes, I am aware that this is some sort of black/white-imaging and the
solution is somewhere in the middle for everyone, but it shows the point much
nicer)

------
jrockway
Well, conference slides and large-scale applications are two different things.
The advantage of the MOP manipulation is that it's composable. If you want to
add another function to the module, you just call a method. If you want to
make the process configurable, you take a callback as an argument and call it
as you are building the module. It's just programming.

eval, on the other hand, is limited to whatever string the programmer types
in. This may be shorter for a conference slide, but probably more painful in
Real Software.

------
dnaquin
Is it just me or does the closure version read more clearly?

~~~
pvg
It seems the purpose was to show three module definitions and then and then
talk about them - in which case both the eval and meta versions are complete
gibberish since they have absolutely nothing to do with his expository goals.
Both basically amount to 'let me show you some cute Ruby code that would
generate the code that I could have just shown you on three slides. Instead of
showing you three slides, though, I'd like you to become a Ruby interpreter
for a few moments'. This seems almost purposefully obtuse.

As to eval itself, it's obviously not 'evil'. But nontrivial use can make code
unreadable:

    
    
      doSomeStuff()
      eval(someVar)
      doOtherStuff()
    

this can be read as

    
    
      doSomeStuff()
      doPrettyMuchAnythingConceivable()
      doOtherStuff()
    

This neatly removes any ability to effectively reason about the code.

------
cduan
The practical problem with eval() is that people overuse it. This led me to
once find a wonderful little code fragment written by a codeveloper that, when
boiled down, was:

    
    
      eval(":" + symbol.to_s)
    

which converted a symbol to a ... symbol.

------
joe_the_user
The standard Ruby interpreter never releases memory to system once it
allocates it. Ever. This is expected behavior.

One of the founders of Twitter mentioned seeing an Engine Yard guy copy a 500
meg file from one machine to another. That instance of the interpreter
allocated 500 megs and wouldn't let it go till it is killed.

All this is the result of Ruby having to support Eval().

I love Ruby. I don't program in it anymore and I think that's tragic. I can
see how Lisp is great just in the since that in a homoiconic language, eval is
OK.

But Ruby is nice and so much more intuitive, productive and beautiful and
_welcoming_ than those homoiconic langs.

Perhaps this is why _why vanished... The Marcel Proust of the 21st century
needs to write a tragic .... _user's manual_ on the subject.

~~~
jfoutz
Sorry, maybe i'm just not thinking clearly this late at night. Why in the
world is supporting eval related to hanging on to 500 megs of unused memory?

Ruby is neat and all, but is there really anything in ruby that's not dressed
up smalltalk? Squeak doesn't randomly hang on to 500m, afaik.

------
edw519
The only thing missing from eval() is a keyboard shock to the programmer who
tries to put a GOTO inside of it.

~~~
fogus
It does... except that the shock is lazy and is only felt by the next person
trying to understand it.

------
spc476
Yeah, it's not evil ( <http://boston.conman.org/2009/08/30.1> ). Nope, not in
the slightest ( <http://boston.conman.org/2009/09/07.1> ).

~~~
rimantas
I think you have missed "quite" from the title, and this bit from the article:

 _I hear two complaints most often when this talk comes up. The first is that
eval() is dangerous. That usually means that we could take in some malicious
code from a user and pass it into eval() where it would destroy our hard
drive, or worse. Only, I'm not doing that. I made the variables here and user
input is not involved. I think we're safe this time._

