28

"Is 'eval' supposed to be nasty?" inspired this one:

Mostly everybody agrees that eval is bad, and in most cases there is more elegant/safer replacement.

So I wanted to ask: if eval is misused that often, is it really needed as a language feature? Is it doing more evil than good?

Personally, the only place I find it useful is to interpolate strings provided in config file.

Edit: The intention of this question is to get as many real-life cases as possible when eval is the only or the best solution. So please, don't go into "should a language limit a programmer's creativity" direction.

Edit2: And when I say eval, of course I refer to evaling string, not passing ruby block to instance_eval or class_eval.

Community
  • 1
  • 1
Mladen Jablanović
  • 43,461
  • 10
  • 90
  • 113

11 Answers11

28

The only case I know of (other than "I have this string and I want to execute it") is dynamically dealing with local and global variables. Ruby has methods to get the names of local and global variables, but it lacks methods to get or set their values based on these names. The only way to do AFAIK is with eval.

Any other use is almost certainly wrong. I'm no guru and can't state categorically that there are no others, but every other use case I've ever seen where somebody said "You need eval for this," I've found a solution that didn't.

Note that I'm talking about string eval here, by the way. Ruby also has instance_eval, which can take either a string or a block to execute in the context of the receiver. The block form of this method is fast, safe and very useful.

Chuck
  • 234,037
  • 30
  • 302
  • 389
  • 2
    I know that this goes beyond the scope of the primary question, but I'm curious in which real-usage cases you need to dynamically assign local/global variables, i.e. when using instance attributes and appropriate methods instance_variable_set and instance_variable_get doesn't work? – Mladen Jablanović Dec 14 '09 at 20:16
  • Well, you don't always want to store things past the method invocation, and sometimes you want to use local variables to determine what ivars get set to. One place where this can be useful is to eliminate boilerplate. Often an initializer will take arguments and assign them to identically named instance variables. With `eval`, you could abbreviate this to something like `set_ivars(binding)`. – Chuck Dec 14 '09 at 20:30
  • this idiom is useful, and only possible with eval: `eval('self', block.binding)` – horseyguy Oct 03 '10 at 21:38
  • There is discussion to remove `Proc#binding` altogether. I'm still not convinced about a good use of `eval` to access local variables. – Marc-André Lafortune Jan 31 '13 at 19:53
  • @horseyguy I'm pretty sure that's just `binding.receiver` (maybe this is a new method added since your comment?) :) – mltsy Apr 04 '18 at 23:14
  • 1
    @mltsy look at the date i wrote that comment, 2010 :) The `binding.receiver` method did not exist then! :) – horseyguy Apr 04 '18 at 23:16
18

When is it justified? I'd say when there's no reasonable alternative. I was able to think of one use where I can't think of an alternative: irb, which, if you dig deep enough (to workspace.rb, around line 80 in my copy if you're interested) uses eval to execute your input:

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

That seems pretty reasonable to me - a situation where you specifically don't know what code you're going to have to execute until the very moment that you're asked to do so. Something dynamic and interactive seems to fit the bill.

Mike Woodhouse
  • 51,832
  • 12
  • 88
  • 127
14

The reason eval is there is because when you need it, when you really need it, there are no substitutes. There's only so much you can do with creative method dispatching, after all, and at some point you need to execute arbitrary code.

Just because a language has a feature that might be dangerous doesn't mean it's inherently a bad thing. When a language presumes to know more than its user, that's when there's trouble.

I'd argue that when you find a programming language devoid of danger, you've found one that's not very useful.

When is eval justified? In pragmatic terms, when you say it is. If it's your program and you're the programmer, you set the parameters.

tadman
  • 208,517
  • 23
  • 234
  • 262
  • 3
    The only thing I might add is "it's justified when you say it is AND you are aware of the ramifications". – Bryan Oakley Dec 14 '09 at 19:02
  • My intention was to get as many justified eval usages as possible, not to argue whether Ruby author(s) did the right thing. But if you want to go there, I'd say that often I am not the only one who set the parameters, as you say, as often I am not the only programmer on the project. And eval is one of the most dangerous language features, fallen in wrong hands. – Mladen Jablanović Dec 14 '09 at 19:25
  • 1
    -1. Generalizations and putting the ball back in MLaden's court don't help anyone. – Mark Bolusmjak Dec 14 '09 at 19:28
  • @z5h I'm not sure how what I said is an invalid answer. If you're working on a project, there will be many conventions and parameters you will have to conform to, one of which might be to not use eval, but that really is someone's judgement call. I did my best to answer the question of why eval is even part of Ruby, or when you can use it. As the post here is rather philosophical in nature, it seems peculiar you're offended by a philosophical answer. – tadman Dec 14 '09 at 20:43
  • Not at all offended. And I'm not implying your post was invalid. I just felt the generalizations were not helpful in this case. – Mark Bolusmjak Dec 15 '09 at 05:04
  • 1
    @z5h +1. I felt the generalizations WERE helpful in this case, and I'm somebody. :P – BobRodes Jan 25 '20 at 23:11
  • "If it's your program and you're the programmer, you set the parameters." Wow - it turns out all those mistakes I made in my earlier programming years, all those mistakes that wasted my time and sometimes others, those were all justified because I was the programmer! – David Ljung Madison Stellar Dec 06 '21 at 23:14
  • @DavidLjungMadisonStellar Don't confuse "justified" with "is your responsibility to think through". This is your call as the developer, but you can obviously make the wrong call. – tadman Dec 08 '21 at 00:34
  • @tadman Someone without experience/wisdom/information is unlikely to come to the right answer simply by following their "responsibility to think through" and hence their decision is quite possibly incorrect. I don't see that as justified. The asker was wise enough to realize that they didn't have the experience/wisdom/information to determine what would be the best practice when they decided to post a question here, so telling them that whatever they "responsibly think through" will be justified seems like a naive response that is unlikely to be the desired answer. – David Ljung Madison Stellar Dec 08 '21 at 03:52
  • @DavidLjungMadisonStellar Like when flying someone is "in command" and their decisions must be carefully considered or there will be serious consequences, the same goes for programming. The good news is we can have code reviews. The responsibility is *always* up to the programmer. Of course, they can be supported or hindered by the surrounding process, and are expected to operate within whatever parameters they've been given. – tadman Dec 08 '21 at 23:19
  • Yes - and this programmer came here for a code review and was told that anything they decided, as long as they were careful, was correct. I fundamentally disagree with that. – David Ljung Madison Stellar Dec 10 '21 at 00:56
6

There is one very important use-case for eval() which cannot (AFAIK) be achieved using anything else, and that is to find the corresponding object reference for a binding.

Say you have been passed a block but (for some reason) you need access to object context of the binding, you would do the following:

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

It is also useful to define the following:

class Proc
    def __context__
        eval('self', self.binding)
    end
end
horseyguy
  • 29,455
  • 20
  • 103
  • 145
5

IMO mostly for Domain Specific Languages.

"Evaluation Options in Ruby" is an article by Jay Fields about it on InfoQ.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Diego Dias
  • 21,634
  • 6
  • 33
  • 36
4

eval is a tool, it is neither inherently good nor evil. It is justified whenever you are certain it is the right tool for what you are trying to accomplish.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • I mostly agree. The problem is when those who abuse the eval-hammer start breaking things just because they don't know the language; like, they rewrite the RTL or something. – Robert K Dec 14 '09 at 19:25
  • @The Wicked Flea: that's why I included "whenever you are certain". If you think it's the right tool, it's not. It's only when you know it's the right tool and know the ramifications of that decision that it is indeed the right tool. – Bryan Oakley Dec 14 '09 at 20:06
  • Well, being certain doesn't automatically make your decisions justified. Also, I'd argue that some tools are being used for benign purpose more often than others (a plow and a bomb come to mind). Heh, a political comment for a political answer. :( – Mladen Jablanović Dec 14 '09 at 20:47
  • Right on. Just because tools are dangerous doesn't mean they're not useful. A power saw, by way of analogy, is extremely dangerous, but at the same time, an invaluable tool. – tadman Dec 14 '09 at 20:47
2

In general eval is a useful language feature when you want to run arbitrary code. This should be a rare thing but maybe you are making your own REPL or you want to expose the ruby run-time to the end user for some reason. It could happen and that is why the feature exists. If you are using it to work around some part of the language (e.g. global variables) then either the language is flawed or your understanding of the language is flawed. The solution is typically not to use eval but to either better understand the language or pick a different language.

It's worth noting that in ruby particulary instance_eval and class_eval have other uses.

olleicua
  • 2,039
  • 2
  • 21
  • 33
2

A tool like eval is about evaluating code at runtime vs. "compile" time. Do you know what the code is when you launch Ruby? Then you probably don't need eval. Is your code generating code during runtime? then you probably need to eval it.

For example, the methods/functions needed in a recursive decent parser depend on the language being parsed. If your application builds such a parser on-the-fly, then it might make sense to use eval. You could write a generalized parser, but it might not be as elegant a solution.

"Programatically filling in a letrec in Scheme. Macros or eval?" is a question I posted about eval in Scheme, where its use is mostly unavoidable.

Community
  • 1
  • 1
Mark Bolusmjak
  • 23,606
  • 10
  • 74
  • 129
  • 2
    You can generate functions at runtime without using eval. – Chuck Dec 14 '09 at 19:29
  • Write code that generates a set of mutually recursive functions without eval. Then with eval. My contention is that the second exercise will usually be simpler. – Mark Bolusmjak Dec 14 '09 at 19:33
  • In Ruby, the way to generate methods with and without `eval` is almost the same, so I'm not sure why this would be different? – sarahhodne Dec 14 '09 at 19:53
  • I'm talking about code that completely *generates* the string to be eval-ed. It doesn't just fill in a couple values in a template then evals. Especially when that generated code contains a set of mutually recursive functions (they all call into each other). Also, I never said it's the only way to do things, just possibly simpler. – Mark Bolusmjak Dec 14 '09 at 20:02
  • I would actually like to test that hypothesis, but no cases where I'd want to do what you're describing come to mind. Can you think of any where the whole effort wouldn't just be needless gold-plating? – Chuck Dec 14 '09 at 20:08
  • I posted a link to an example I came up with in Scheme and discussed on SO. I feel the situation would be similar. Even in my example, it's not totally needed, but then I'd incur the overhead of doing checks in a function every time, when instead I can do them ahead of time, write out the functions and eval once. – Mark Bolusmjak Dec 14 '09 at 20:25
2

You very likely use eval on a regular basis without even realizing it; it's how rubygems loads the contents of a Gemspec. Via rubygems/lib/specification.rb:

  # Note: I've removed some lines from that listing to illustrate the core concept
  
  def self.load(file)
   
    code = File.read(file)
    begin
      _spec = eval code, binding, file       #  <--------  EVAL HAPPENS HERE 

      if Gem::Specification === _spec
        return _spec
      end

      warn "[#{file}] isn't a Gem::Specification (#{_spec.class} instead)."
    rescue SignalException, SystemExit
      raise
    rescue SyntaxError, Exception => e
      warn "Invalid gemspec in [#{file}]: #{e}"
    end

    nil
  end

Typically, a gem specification would look like this:

Gem::Specification.new do |s|
  s.name        = 'example'
  s.version     = '0.1.0'
  s.licenses    = ['MIT']
  s.summary     = "This is an example!"
  s.description = "Much longer explanation of the example!"
  s.authors     = ["Ruby Coder"]
  s.email       = 'rubycoder@example.com'
  s.files       = ["lib/example.rb"]
  s.homepage    = 'https://rubygems.org/gems/example'
  s.metadata    = { "source_code_uri" => "https://github.com/example/example" }
end

Note that the gemspec file simply creates a new object but does not assign it nor send it anywhere. Trying to load or require this file (or even executing it with Ruby) will not return the Gem::Specification value. eval is the only way to extract the value defined by an external ruby file.

Ian
  • 11,280
  • 3
  • 36
  • 58
0

One use of eval is compiling another language to ruby:

ruby_code = "(def foo (f a b) (mapv f (cons a b)))".compile_to_ruby
# "foo_proc = ->(f a b) { mapv_proc.call(f, (cons_proc.call(a, b)) }"
eval ruby_code
conanite
  • 1
  • 1
0

I use a 3D modeling software that implemented Ruby for writing custom text macros. In that software we are given access to model data in the form of name:value pairs accessed using the following format:

owner.name
#=> value

So for a 36 inch tall cabinet, I could access the height and convert its value to feet like so:

owner.height.to_f / 12

The main problem is that objects in that software have no unique identifiers aside from something called their schedule_number. If I want to name a variable using the schedule_number in the variable name so that I can call and use that value elsewhere, the only possible way I know to do that is by using eval:

eval "#{owner.schedule_number} = owner.height"
Burak Kaymakci
  • 662
  • 2
  • 16
  • 36