Reputation: 12104
Here my general focus is Scala and Lisp/Scheme macros, not exactly the ones in the C/C++/Obj-C
I just don't see the point.
The way I understand it, is that macros are there to extend the language. But so are functions.
I understand that some things can't be implemented cleanly due to some language limitations, and as such a macro needs to be the way to go. But a LOT of examples I see with macros seem to be things that are fairly simple to implement using normal functions.
So what exactly is the purpose? Clean macros or otherwise, Someone please enlighten me. If possible, please provide some example code of something that can be done in macros, but is impossible/hard to do with normal functions.
Upvotes: 6
Views: 827
Reputation: 51501
There are three main purposes that macros are used for in Lisp (don't know about Scala):
defun
, defgeneric
, defclass
(from the standard), deftable
(from postmodern).Unwind-protect
wrappers: a state is temporarily modified and ensured to be modified back after completion of a task. This can be cumbersome to write repeatedly, so we create a shorthand. Example: with-open-file
(standard), with-transaction
(many database libraries).Concrete example: Java 7 introduced a shorthand for ensuring the closing of Closable
s in try
-blocks:
try (SomeClosable foo = openFoo()) {
foo.doSomething();
}
which can in Java 6 only be expressed roughly like this:
SomeClosable foo;
try {
foo = openFoo();
foo.doSomething();
} finally {
if (foo != null && foo.isOpen()) {
foo.close();
}
}
Java developers had to wait for the language designers to implement this feature. A Lisp developer uses a little macro:
(defmacro with-open-foo ((var &rest options) &body body)
`(let ((,var (open-foo ,@options)))
(unwind-protect
(progn ,@body)
(when ,var (close ,var)))))
so that he can write
(with-open-foo (f :bar baz)
(do-some-foo f)
(and-something-else))
instead of
(let ((f (open-foo :bar baz)))
(unwind-protect
(progn
(do-some-foo f)
(and-something-else))
(when f (close f))))
Upvotes: 1
Reputation: 48745
In both Common Lisp and Scheme most of the special syntax are indeed implemented in terms of other special syntax, thus macros.
For instance both Scheme and CL have if
, cond
and case
but only if
is a primitive syntax.
There is nothing special about macros defined by the standard and the ones you might make yourself. They can be made to behave and work just as good as primitives.
Macros has a cost of obfuscating and surprising the reader. Using common naming conventions like with-*
might help a bit but one should never use a macro if a function/procedure can do the job or if the form will only be used in a few number of places.
Upvotes: 2
Reputation: 13048
There is a concise summary of what things have been done with Scala macros: http://scalamacros.org/paperstalks/2014-02-04-WhatAreMacrosGoodFor.pdf. To sum it up, macros are known to be good for: 1) code generation, 2) advanced static checks, 3) empowering domain-specific languages. Having macros in Scala to be type-based provides an additional and powerful twist to the capabilities typically found in macros in Lisp-like languages.
Some examples in the slides linked above can indeed be implemented without macros, but the result will be either lacking in some sense (e.g. in performance) or overly complicated for the users (e.g. because of heavyweight error messages). For example, typed channels for Akka could be conceivably implemented with pure implicits, but compilation speeds and understandability would suffer. Or, scala/async could be implemented as a compiler plugin, but then it would have to depend on internal compiler APIs and would be harder to distribute.
Of course, macros are not the silver bullet. There clearly are use cases when they are not the best choice, and this is something that's outlined in http://scalamacros.org/paperstalks/2014-03-01-MacrosVsTypes.pdf. What's curious, though, is that in a number of situations neither pure-macro, nor macro-less solutions, but rather carefully constructed hybrids end up being the best.
Upvotes: 7