Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

In Common Lisp, defmacro basically allows us to build our own DSL.

I read this page today and it explains something cleverly done:

But I wasn't about to write out all these boring predicates myself, so I defined a function that, given a list of words, builds up the text for such a predicate automatically, and then evals it to produce a function.

Which just looks like defmacro to me.

Is eval the defmacro of javascript? Could it be used as such?

share|improve this question
    
defmacro sounds like a preprocessor directive / macro. eval is just a function which allows you to execute javascript dynamically, at runtime. I guess you could write an interpreter in it, but it wouldn't be natively supported in any JS environment - you'd have to make a call to your code, and encode everything inside a JS string. –  Daniel B Oct 5 '12 at 13:14
    
@DanielB Well, defmacro is a preprocessor macro in Common Lisp. It's what makes CLisp so famous and powerful. That's why using eval in a similar way in javascript seems appealing to me, even though it's technically not implemented in the same way. I'd just like some community opinion about this. –  Florian Margaine Oct 5 '12 at 13:42
    
I understand, my point is that defmacro allows you to code macros "naturally" in lisp, without going into any special mode, while with eval, you'd have to essentially code inside a string (e.g. how a regular expression is used in C#). It's certainly doable (and has been done before - e.g. coffeescript can be turned into JS using JS itself, and eval'd dynamically) although I suspect you will find heavy resistance in the community, who have been trained to avoid eval like the plague. –  Daniel B Oct 5 '12 at 14:05

2 Answers 2

up vote 11 down vote accepted

It can, but it's more dangerous.

defmacro has three main features:

  1. It runs at compile time;
  2. It is a genuine in-place substitution; and
  3. Because of the whole nature of Lisp syntax, it works at an AST level, not a source-code level.

By contrast, eval:

  1. Executes at runtime;
  2. Is not an in-place substitution; and
  3. Works with strings.

Why does this matter?

First, eval has a speed penalty: because it has to evaluate its arguments every single time it's called, instead of just once at compile time, you'll have a speed hit on each run of the program.

Second, there is no compile-time validation that the eval is actually getting passed something sane. You can have a horrible syntax error in your eval argument (or, worse, a horrible syntax error only when called in certain ways), but it won't be caught until runtime far down the road. Because Lisp macros are compile-time, you can't have that. Your macro might not work, mind you, but it'll compile by definition to something valid.

Third, eval has serious safety issues compared to defmacro. Because Lisp macros work with the AST, there's a high degree of safety. No matter what argument you supply the macro at runtime, those arguments can't allow a malicious caller to "escape" the macro, or call malicious code. That's not true of eval: if you don't think through your string escapes very, very carefully, and you allow any form of user-generated input into the eval string, you're risking allowing malicious code to run.

Okay, but, whatever, I can hear you say. The user would have to do that to himself, and it's already his browser, so why care?

Because there are lots of ways to inject scripts in that situation. Cross-site script requests, for example, or forgetting to escape a string properly that you store in a database. There are lots of ways of getting malicious values into those macros--and the moment that happens, your users' data is toast.

Finally, even if all of that doesn't dissuade you, JavaScript's eval in particular is simply less powerful. There are odd ways that it interacts with the enclosing variable scope and the global variable scope, for example, and there's no sane way to do the equivalent of Common Lisp's gensym for variable hygiene.

Can you use eval in some of the places you might use defmacro? Sure. But they are hardly interchangeable, and I hope that the above explanation makes it clear why absolutely zero good JavaScript frameworks I know use eval.

Instead, if you really find yourself wanting macros, go find a compiles-to-JavaScript language that has them, such as ClojureScript. That way, you'll get the benefits of Lisp macros without the drawbacks of eval.

share|improve this answer

As eval can execute arbitrary javascript I'm sure you could put something together similar to a macro system. That being said, javascript is not homoiconic in the same way as Common Lisp or Scheme. Homoiconicity(?) is the fancy term for the old list mantra of "Code as data". A macro system transforms code. This is very natural when you can maniplate code in the same way that you manipulate other data because everything is a list (LISt Proccessing). We can list many ways in which javascript "is a lisp", but it does not treat code as data.

In a lisp macro system, we manipulate lists. In a "javascript macro system" (notice the quotes) we have to manipulate strings or some complicated AST.

There's a famous antecdote where someone is giving a talk on all the ways that Python is a lisp "first class functions blah blah blah". At the end, John McCarthy (inventor of Lisp) raises his hand and asks "Does it treat code as data?" Zing.

In conclusion, eval could probably be used to tack a macro system onto javascript. But it would not be as natural as Common Lisp's defmacro.

share|improve this answer
1  
Lisp advocates' claims notwithstanding, conflating code with data is not a good thing and never has been. It's a massive security hole that's been responsible for billions of dollars worth of losses over the last couple decades. Every time you hear about a major website or a bank or credit card processor that got hacked through SQL injection and disclosed the user data of millions of customers, that's because some programmer somewhere did not properly separate code from data. –  Mason Wheeler Oct 5 '12 at 14:23
4  
@MasonWheeler That's not all what "code as data" means. It has nothing to do with security. It means you can manipulate your code using the same tools you use to manipulate your data. In Lisp, code and data have the same form: lists. I can write a macro to manipulate a function in the same way I could write a function to manipulate a list or tree. SQL injections happen when you don't sanitize input. Including raw SQL in your code isn't "code as data". –  axblount Oct 5 '12 at 14:29
    
No, SQL injections do not happen when you "don't sanitize your input." They happen when you mix data (user input) and code (SQL queries) together as the same thing instead of treating them correctly, as two completely separate concepts, by using parameters. Sanitizing is of very limited usefulness compared to doing it right by properly separating code and data. –  Mason Wheeler Oct 5 '12 at 14:36
4  
@MasonWheeler I agree. There are better ways to handle user input than "sanitizing" a string and inserting it into the SQL. However, the issue you're talking about is completely unrelated to the Lisp concept of "code as data". –  axblount Oct 5 '12 at 14:45
2  
In fact, Lisp-style "code as data" (as the basis of macros) is one way to avoid SQL injections, without having to write clunky-looking code. For example, I plan to write an SQL data-access module that allows you to write queries like (select * from foo where (lcase name) = ,(string-downcase name)), which will get turned into "select * from foo where lcase(name) = ?", with the value filled in by the (string-downcase name) expression. Try doing that with other languages! –  Chris Jester-Young Oct 5 '12 at 17:31

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.