6

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.

Electric Coffee
  • 11,733
  • 9
  • 70
  • 131
  • http://stackoverflow.com/questions/267862/what-makes-lisp-macros-so-special?s=6|1.8097 http://stackoverflow.com/questions/5833033/in-lisp-code-is-data-what-benefit-does-that-provide/5833388?s=11|1.6415#5833388 – Rainer Joswig Nov 04 '14 at 11:05
  • duplicate of http://stackoverflow.com/questions/2561221/examples-of-what-lisps-macros-can-be-used-for?s=7|4.5505 – Rainer Joswig Nov 04 '14 at 11:07

3 Answers3

7

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.

Eugene Burmako
  • 13,028
  • 1
  • 46
  • 59
  • There are several examples in the first presentation. I don't think that 1, 2, 5, 7 and 8 are implementable without macros. 3 and 4 can be done with advanced type-level programming, but the result will be slower both at compile-time and runtime and much harder to maintain. 6 can be approximated, but user experience will suffer (error messages, inconsistent vocabulary). – Eugene Burmako Nov 04 '14 at 11:42
2

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.

Sylwester
  • 47,942
  • 4
  • 47
  • 79
1

There are three main purposes that macros are used for in Lisp (don't know about Scala):

  • Defining: something is created and directly registered at the appropriate place. Examples: 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).
  • Generation of other languages: for example, CL-WHO (HTML), Parenscript (JavaScript). By generating the code of other languages in Lisp forms, we can use macros for those other languages even if they do not have support for that themselves.

Concrete example: Java 7 introduced a shorthand for ensuring the closing of Closables 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))))
Svante
  • 50,694
  • 11
  • 78
  • 122
  • wouldn't you be able to just write with-open-foo as a function using `defun`? – Electric Coffee Nov 04 '14 at 10:48
  • @ElectricCoffee: simple answer: no. Longer answer: you can do something that goes in that direction using callbacks, but it is more complicated to write, more cumbersome to use, and involves two additional function invocations. – Svante Nov 04 '14 at 11:22
  • sorry to revive a dead topic, but I still don't see _how_ your example benefits from code-generation, and can't just be implemented using a function call. I mean why won't a function with variable args not work in this case? Could you show how it would be cumbersome to use and such? – Electric Coffee Mar 25 '15 at 11:44