Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What makes Lisp macros so special?

Reading Paul Graham's essays on programming languages one would think that Lisp macros are the only way to go. As a busy developer, working on other platforms, I have not had the privilege of using Lisp macros. As someone who wants to understand the buzz, please explain what makes this feature so powerful.

Please also relate this to something I would understand from the worlds of Python, Java, C# or C development.

like image 460
minty Avatar asked Nov 06 '08 07:11

minty


People also ask

What is so special about Lisp macros?

The special power that Lisp macros have is that they can control evaluation (as seen by evaluating the input expression via ~expr and do arbitrary source-to-source transformations with the full power of the language available.

When would you use a macro Lisp?

A macro call involves computation at two times: when the macro is expanded, and when the expansion is evaluated. All the macroexpansion in a Lisp program is done when the program is compiled, and every bit of computation which can be done at compile-time is one bit that won't slow the program down when it's running.

What makes Lisp unique?

Lisp programs are trees of expressions, each of which returns a value. (In some Lisps expressions can return multiple values.) This is in contrast to Fortran and most succeeding languages, which distinguish between expressions and statements.

Are rust macros like Lisp macros?

Rust's macros are very good. They act like Lisp's macros, unlike Haskell's. The fact that Rust has type-classes (“traits”) and sum types (“enums”) and pattern matching is very attractive.


1 Answers

To give the short answer, macros are used for defining language syntax extensions to Common Lisp or Domain Specific Languages (DSLs). These languages are embedded right into the existing Lisp code. Now, the DSLs can have syntax similar to Lisp (like Peter Norvig's Prolog Interpreter for Common Lisp) or completely different (e.g. Infix Notation Math for Clojure).

Here is a more concrete example:
Python has list comprehensions built into the language. This gives a simple syntax for a common case. The line

divisibleByTwo = [x for x in range(10) if x % 2 == 0] 

yields a list containing all even numbers between 0 and 9. Back in the Python 1.5 days there was no such syntax; you'd use something more like this:

divisibleByTwo = [] for x in range( 10 ):    if x % 2 == 0:       divisibleByTwo.append( x ) 

These are both functionally equivalent. Let's invoke our suspension of disbelief and pretend Lisp has a very limited loop macro that just does iteration and no easy way to do the equivalent of list comprehensions.

In Lisp you could write the following. I should note this contrived example is picked to be identical to the Python code not a good example of Lisp code.

;; the following two functions just make equivalent of Python's range function ;; you can safely ignore them unless you are running this code (defun range-helper (x)   (if (= x 0)       (list x)       (cons x (range-helper (- x 1)))))  (defun range (x)   (reverse (range-helper (- x 1))))  ;; equivalent to the python example: ;; define a variable (defvar divisibleByTwo nil)  ;; loop from 0 upto and including 9 (loop for x in (range 10)    ;; test for divisibility by two    if (= (mod x 2) 0)     ;; append to the list    do (setq divisibleByTwo (append divisibleByTwo (list x)))) 

Before I go further, I should better explain what a macro is. It is a transformation performed on code by code. That is, a piece of code, read by the interpreter (or compiler), which takes in code as an argument, manipulates and the returns the result, which is then run in-place.

Of course that's a lot of typing and programmers are lazy. So we could define DSL for doing list comprehensions. In fact, we're using one macro already (the loop macro).

Lisp defines a couple of special syntax forms. The quote (') indicates the next token is a literal. The quasiquote or backtick (`) indicates the next token is a literal with escapes. Escapes are indicated by the comma operator. The literal '(1 2 3) is the equivalent of Python's [1, 2, 3]. You can assign it to another variable or use it in place. You can think of `(1 2 ,x) as the equivalent of Python's [1, 2, x] where x is a variable previously defined. This list notation is part of the magic that goes into macros. The second part is the Lisp reader which intelligently substitutes macros for code but that is best illustrated below:

So we can define a macro called lcomp (short for list comprehension). Its syntax will be exactly like the python that we used in the example [x for x in range(10) if x % 2 == 0] - (lcomp x for x in (range 10) if (= (% x 2) 0))

(defmacro lcomp (expression for var in list conditional conditional-test)   ;; create a unique variable name for the result   (let ((result (gensym)))     ;; the arguments are really code so we can substitute them      ;; store nil in the unique variable name generated above     `(let ((,result nil))        ;; var is a variable name        ;; list is the list literal we are suppose to iterate over        (loop for ,var in ,list             ;; conditional is if or unless             ;; conditional-test is (= (mod x 2) 0) in our examples             ,conditional ,conditional-test             ;; and this is the action from the earlier lisp example             ;; result = result + [x] in python             do (setq ,result (append ,result (list ,expression))))            ;; return the result         ,result))) 

Now we can execute at the command line:

CL-USER> (lcomp x for x in (range 10) if (= (mod x 2) 0)) (0 2 4 6 8) 

Pretty neat, huh? Now it doesn't stop there. You have a mechanism, or a paintbrush, if you like. You can have any syntax you could possibly want. Like Python or C#'s with syntax. Or .NET's LINQ syntax. In end, this is what attracts people to Lisp - ultimate flexibility.

like image 85
gte525u Avatar answered Sep 27 '22 18:09

gte525u