The root of all eval
strangelyconsistent.org
strangelyconsistent.org
my &eval = &EVAL
then that is largely moot.After that, fragments like this suddenly make sense:
n = 1
/1*"\/\//.test(n + '"//')
[0]: https://www.ecma-international.org/ecma-262/5.1/ [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.
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).
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.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?
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?
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.
* 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...
To the second part of your question: as you have undoubtedly noticed, you've never heard of E nor Monte.
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.
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 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.