Hacker News new | past | comments | ask | show | jobs | submit login
On eval in dynamic languages generally and in Racket specifically (2011) (racket-lang.org)
48 points by soegaard on July 28, 2014 | hide | past | favorite | 18 comments



Lisp is probably the only language where eval makes sense. If you need to concatenate strings together to create "code", you're doing it wrong.


What you mean is that it only makes sense in homoiconic languages; Lisp is not the only one[1]. In particular, there's Julia, now[2].

[1] http://en.wikipedia.org/wiki/Homoiconicity#Examples

[2] http://docs.julialang.org/en/latest/manual/metaprogramming/


That reminds me of the ancient Infix module for Common Lisp.

    [1]> (load "infix")
    T
    [2]> (let ((a 1) (b 2) (c 3)) #i(a*b+c))
    5
    [3]> '#i(a*b+c)
    (+ (* A B) C)
Like the colon in Julia, we can quote the infix (#i) expression, to reveal its AST.

Infix URL:

http://www.cs.cmu.edu/afs/cs/project/ai-repository/ai/lang/l...

Infix has f(x,y) type function calls and a[i,j] type array referencing and whatnot.

It's not really homoiconicity because the AST doesn't resemble the language being quoted. The AST and its printed notation are homoiconic; the LALR(1) statement/expression/infix cruft layered on it arguably isn't.


For adding infix to s-exps, "sweet expressions" are a much cleaner approach: http://www.dwheeler.com/readable/


I'm sure I've seen that before. Anyway, it's destined for the same dustbin as infix.cl. These things have value in showing "see, you can do this, but Lisp programmers don't actually use it". Before infix.cl and Readable, back in the 1970's, there was also something called CGOL[1]. This was described in a 1976 MIT paper by Pratt R. Vaughan titled CGOL: An Alternative External Representation for LISP Users.

And, of course, Lisp wasn't supposed to wear its S-expression underwear on the outside in the first place; M-expressions were supposed to be used for programming.

[1] http://en.wikipedia.org/wiki/CGOL


You could assemble an AST in a non-homoiconic language. This has some added complexity over something like Lisp to be sure, but it doesn't have most of the downsides of concatenating strings.



Very nice. Template Haskell takes a similar approach, though is (usually) not run-time.


I've seen it used to decent effect in Javascript templating libraries.


Even more so in proto-spreadsheets.


I think I can argue that a raw "eval" is always going to be the worst way to achieve a given result.

Since eval calls the entire system to do something, it involves every possible danger that programming the system ordinarily involves and it involves dangers particular to it (not being able to directly check the syntax any of the code beforehand, constructing values with strings, the possibility the scope of variable-binding may vary from call to call, etc). The danger is inherent in the approach.

This isn't a reason to never use eval, just a reason to never use it if you can think of an alternative. Sometimes you can't find an alternative in the time allotted.

And if you use eval or any equivalent, just remember you have opened a bottomless can of worms. We have spent generations now cleaning up SQL injections, for example.


It's not just the danger. In sufficiently dynamic languages, it wreaks havoc on optimization unless you're prepared to essentially re-jit the entire code.

Ruby for example - if you enter eval() with a string, you may exit it to find that even basic stuff like adding two integers together has changed (and may suddenly have side effects), so any work you've done to inline (or cache) fast paths may suddenly have been undone. It's not likely that the worst changes will have happened, but they may, and so unless the string is constant and you can analyse it upfront, you need to be prepared for them unless you want to restrict yourself to a subset of the language.


One case I know of where eval is used extensively is Python's namedtuple function from the collections module. It generates a class that behaves like a tuple, except the positions are named, so kind of like a read-only struct. Python has the __slots__ facility for exactly this situation - when you know exactly how many members your instances will have and don't want extensibility, but when namedtuple was written, it wasn't possible to programatically generate a class body with __slots__ in any way other than to generate the source for it and call eval. So here we are.


Indeed, some things can't be done unless you use eval; but some of those things just need something much less than eval added to the language and then they can be done.

Case in point: if you don't have apply in Lisp, how do you take a list, and turn it into the arguments of a function call?

The ability to do this is actually represents a modicum of introspection, but far short of a full blown eval.


    ;;; The Daily WTF to the rescue, no eval or introspection needed
    (defun my-apply (f lst)
      (case (length lst) 
        (0 (funcall f))
        (1 (funcall f (nth 0 lst)))
        (2 (funcall f (nth 0 lst) (nth 1 lst)))
        (3 (funcall f (nth 0 lst) (nth 1 lst) (nth 2 lst)))
        (t 'YouNeedToExtendThisInTheObviousWay)))
...IIRC Common Lisp allows your implementation to limit the number of arguments a function can take.

http://www.ai.mit.edu/projects/iiip/doc/CommonLISP/HyperSpec...


eval is not a good idea if you want to write a compiler that generates small efficient binaries either. This is why, for example the Stalin Scheme compiler does not include eval.

"Running that image has equivalent semantics to loading the Scheme source file into a virgin Scheme interpreter and then terminating its execution. The chief limitation is that it is not possible to LOAD or EVAL new expressions or procedure definitions into a running program after compilation. In return for this limitation, Stalin does substantial global compile-time analysis of the source program under this closed-world assumption and produces executable images that are small, stand-alone, and fast."

https://github.com/barak/stalin


That's worth bringing up, but it's not eval that causes a problem, it's access to environments, especially mutable access. You can implement eval as an interpreter in your own Scheme program, so supplying it as primitive can have no extra consequences; conversely if you have first-class environments, passing (the-environment) to some opaque procedure would do all the damage to analyzability already.


Please don't consider every usage of the term "eval" to be what is discussed in this article. Perl has two forms of eval, one which takes a string and executes it as code (the "bad" one) and one which takes a block of code (used for exception handling) which is parsed only once (essentially at compile time).

http://perldoc.perl.org/functions/eval.html




Consider applying for YC's Spring batch! Applications are open till Feb 11.

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

Search: