Hacker News new | past | comments | ask | show | jobs | submit login
The root of all eval (strangelyconsistent.org)
68 points by jsnell on April 23, 2017 | hide | past | favorite | 38 comments



"It's just a normal function." No, it has magical access to the lexical scope.


If eval appears to have same access if defined like in the article:

   my &eval = &EVAL
then that is largely moot.


Its possible that's a bug and not an intentional design decision. I certainly hope it is, because I agree with you that it doesn't make sense to hide it behind a pragma but have this workaround.


Isn't eval required to run in the null lexical environment? Or is that only true in Lisp?


In JavaScript it depends on how it is invoked: eval(a) has access to scope variables but x=eval;x(a) does not.


I am surprised that JavaScript can still surprise me.


I suggest reading the spec. 5.1 [0] although dated is written in very easy to understand language (well, most parts).

After that, fragments like this suddenly make sense:

    n = 1
    /1*"\/\//.test(n + '"//')

[0]: https://www.ecma-international.org/ecma-262/5.1/


My favorite bit of wat.js is this:

    [1,2,3] + [4,5,6] // => "1,2,34,5,6"
I mean, it makes some degree of logical sense (toString -> concat) but there is no reason that would ever be the intended behavior.

As for that expression, meh. You can write intentionally obtuse code in any language. The problem with JS is the behavior that is different from what you'd expect.


No, in perl eval explicitly has access to the local scope. This is used for capturing otherwise fatal errors in perl (it can be used similarly to try/catch).


Wouldn't the handlers for exception handling by dynamically scoped, not lexically?

If we have a try block, and call a function, and if that function throws, we want the try block to be able to catch that. The function is not understood as having magic access to an environment; it's a pervasive dynamic environment that is visible to the current execution context at all times, in which exit points for non-local transfers are established.

That's almost the entire point since an error situation that is entirely confined to a lexical scope can be dealt with using some local control flow (some form of goto, or structured derivative thereof).


So with perl 5 there isn't a real exception handler like you'd expect from other languages (I'm unsure of perl 6). It works like the following (program output available at [1]):

    use v5.24.0;
    my $x = 1;
    my $y = "bar";
    
    sub foo { # this sub fails fatally
       die "what is going on here";
    }
    
    say "x is $x, y is $y"; # 1 and bar
    
    eval { # similar to try
      my $y = "baz"; # we have a new lexical scope in here too.
      $x = 2; # but this will change the outside scope since it's also available to us
      say "x is $x, y is $y"; # 2 and baz
      foo();
      $x = 3; # this never happens
    };
    
    if ($@) { # check for an error, similar to catch
       say "CAUGHT ERROR: ", $@; # just parrot the error out for now.
    }
    
    say "x is $x, y is $y"; # 2 and bar
Inside the eval the lexical scope outside is still available and a new one is also available. The error that happens gets stuffed into a special variable and you can choose to handle it or not later. This works the same way if you give eval a string to run also.

[1] https://perlbot.pl/pastebin/pl5u9w


My biggest question about this is did the guy not understand why the flag was called `MONKEY-SEE-NO-EVAL`? Because that name was my favourite part of the article...


I think that the author does get the pun; they made another of a similar calibre in the title of the post!

Their main objection to the pragma would seem to be the MONKEY-SEE-NO-EVAL name; if you don't understand the reference, say you're a non-native speaker, or just have no idea what it's referring to, all you'll see is "NO-EVAL", and then get confused why the code suddenly allows EVAL everywhere. Levity and fun is perfectly acceptable, but is it just as acceptable when the language is less obvious for it's extended developer community?


For other people who don't understand either:

https://en.wikipedia.org/wiki/Three_wise_monkeys


The article is called "The root of all eval." I think the author gets it.


To me it looks like the author is oblivious of the pun in MOKNEY-SEE-NO-EVAL, even though he made the same pun in the title.


His point is merely that either this is a seriously bad idea that you should never do, in which case you should not attempt to communicate it through a silly pun, or it is acceptable, in which case it shouldn't be hidden behind a compiler pragma.


I don't think it's possible to be a Perl 6 core developer and not get basic puns like this. #perl6 is nothing if not a series of puns mixed with code.


Yes it's cool but maybe not everybody gets the drift. That and the fact that Perl6 error messages are really meaningful as in not a wordplay on a reference to sanzaru.


Just learned that the 3 wise monkeys are called Sanzaru. Thanks!


If that's your biggest question, then what did you think "weird attempt at levity" referred to?


It's worth noting that eval in perl5 can be used with a codeblock to give basic exception handling, in perl6 this functionality has been moved to try. So while it's quite common to use eval in perl5, I'd expect much less usage in perl6. The name change is probably a good reminder of this.


Perl6 has blocks, so eval already implicitly receives an environment from within which to evaluate.

Why jump up and down screaming eval is super bad, when instead you could have allowed users to specify the context of eval, and made it safe to use?


Is it really going to be safe to use? I don't know Perl that well, but it feels like there's enough language features to make a safe eval harder than writing your own parser for a DSL. That's certainly the case for Python: https://nedbatchelder.com/blog/201206/eval_really_is_dangero...


Python's eval is crippled, it can't take a full environment.

Suppose for a moment that the scope of eval was truly an environment:

And you don't supply the resulting environment with anything but the functions you want.

Not even the dot operator, let alone magic builtins.

Python's eval suddenly becomes much safer. Unfortunately such an eval is harder to implement in Python, because every object carries it's magic methods with it, rather than looking them up elsewhere.


Interesting. Is there any language you or the sibling have used that does this with eval, and does it find much use?


Sure! Most of the time, it just makes it easier to parse and interpret input in a safe manner.

* Racket [0], sometimes used when you don't want to use Racket's way of designing DSLs, or if you want to interface to databases simpler.

* Scheme [1], used when designing DSLs, interacting with databases. You often see it go hand-in-hand with X-Expressions.

* Lua [2] can, but it is rarely used.

Note, the link for Lua actually lists a whole host of languages where this can be technically done, but not all are entirely safe.

I think it's uncommonality is not because of it's inconvenience or rarity, but rather the mantra of 'eval is evil' in the programming community, even when it doesn't apply.

[0] https://docs.racket-lang.org/guide/eval.html

[1] https://www.gnu.org/software/guile/manual/html_node/Environm...

[2] https://rosettacode.org/wiki/Runtime_evaluation/In_an_enviro...


E and Monte have safe eval() as a consequence of being object-based capability-safe languages; there's no way to cause any effect outside eval() worse than an infinite loop.

To the second part of your question: as you have undoubtedly noticed, you've never heard of E nor Monte.


Funny that you say that. When I sat down at my machine at work, one of the 100 or so chrome tabs I had open was a page about The Vat: http://www.erights.org/elib/concurrency/vat.html.



Python wasn't designed from the start with this kind of security concern in mind. It could have been, like http://erights.org/. In such languages it's reasonable to say "you can write a user-level drop-in replacement for eval(), and therefore we can make the built-in eval accessible to all code without any extra security-design concerns" (though of course you still need care in implementation).


A block isn't a sandbox, and the base language itself has everything one needs to shoot gaping holes in feet. I'm not sure how handing off an empty (or whatever) environment to the eval'ed code can be "safe".

I agree with the author that this is probably overkill, in terms of warning about eval, but I'm also not understanding how merely having a block with a block scoped environment can make eval safe.


An empty environment handed to eval would always result in an error, as eval would have no access to syntax or inbuilts or anything else.

Having blocks simply means half the work of implementing eval this way has already been done in the interpreter.

Eval wouldn't have access to the base language if handed an empty environment.


Maybe I don't understand the meaning of "blocks" in Perl 6 (if they work this way, I definitely don't understand them). I've never worked in Perl 6, except a couple of tutorials, so that's entirely possible. I mean, I know Perl 6 has the ability to fundamentally change the language within the language itself.


The only thing blocks have to do with this, is that Perl6 has to internally track state so that closed over values go out of scope when their parent block ends. End of story.

Maybe I can try explaining again.

Eval is unsafe as it reads input values in terms of the current state of a program.

It's environment is implicitly given, and includes the entirety of the Perl6 language, as well as anything user-created.

But, what if the programmer was allowed to supply that context?

Rather than a blacklist against certain bad things, this is a whitelist where you supply every single thing that eval is allowed to use.

Other languages already have this.

Scheme example that works:

    (eval '(+ 1 2) (let (null-environment) '(+ . +)))
Scheme eval that fails:

    (eval '(+ 1 2) (null-environment))
Perl6's flexibility is less unsafe when you don't provide anything flexible.


So in order to use a function that exposes the software to serious undue security risk unless used correctly, the engineer is pushed to do research? That really doesn't seem like a bad thing...


expecting perl not to use silly names for things is like expecting clowns not to wear big shoes


I lol'd.




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: