It can, but it's more dangerous.
defmacro has three main features:
- It runs at compile time;
- It is a genuine in-place substitution; and
- Because of the whole nature of Lisp syntax, it works at an AST level, not a source-code level.
- Executes at runtime;
- Is not an in-place substitution; and
- Works with strings.
Why does this matter?
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.
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.
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