Hyman Rosen said:
Can Lisp macros manipulate the text of the macro arguments?
They don't manipulate text at all, but they _do_ manipulate the
_un_evaluated forms passed to them.
Here is a "top 5" list of why Lisp macros aren't anything like C
preprocessor stuff. At the bottom I give an example.
Top five reasons why Lisp macros are not similar to the C/C++
preprocessor in any meaningful sense.
Number 5:
The/a cpp is a, well, preprocessor. Typically a separate program
even. While you could dilute the meaning here to the point where a
compiler is a preprocessor for a linker is a processor for a loader is
a preprocessor for a cpu etc., the typical meaning is that such things
perform minor transformations on a block of input and had the result
to the primary processor.
Lisp macros are tightly bound up with both the definition of the total
language and any implmentation of it. Also expansion time is
interwoven with compilation, interpretation, or explicit evaluation.
They are available and can be defined during any phase: compile, load,
and execute/runtime.
Number 4:
The/a cpp is not Turing complete.
Lisp macros are Turing complete.
Number 3:
The/a cpp processes text, typically from a text stream such as (most
typically) a text file.
Lisp macros process _data structures_. In particular the syntax trees
output from the parser. Text is long gone before macros are involved
at all.
Number 2:
The/a cpp doesn't know or have any access to the full language, user
defined functions, or its environment.
Lisp macros are just Lisp functions which can make use of the entire
language, and any other user defined functions and macros in libraries
or the running image. They also have access to their lexical
environment information (such as type and profile information) as well
as any public aspects of the global system/application.
And the Number 1 reason why Lisp macros are not similar to the C/C++
preprocessor in any meaningful sense:
Jerry Coffin thinks they are.
;-) and just to make sure
-----------------------------
The astute reader will have noticed that it is C++ _templates_ which
are slighly similar to Lisp macros. And they would be right.
Templates are significantly less expressive and mind numbingly painful
to use in comparison, but they are Turing complete and you can do some
interesting things with them.
For example, while it might take several pages of code, I think it is
_physically_ possible (i.e., humanly doable not just theoretically
doable) to achieve the (somewhat contrived) following example with
templates. Note: I don't see how you can do this _in_ Ada at all.
Allow a user/programmer to define a dictionary like object while
ensuring at compile time that the size of the table implementing it is
a prime number.
(defun small-primep (n)
(and (< 0 n most-positive-fixnum)
(the-primes :from n :to n)))
(deftype prime () `(and integer (satisfies small-primep)))
(defmacro define-dictionary
((name &key (size 101) (test #'eql) (hash #'sxhash)) &body body)
(assert (symbolp name) nil
"Dictionary names must be symbols. ~A is of type ~A"
name (type-of name))
(assert (typep size 'prime) nil
(prime-failure-message size name))
`(defvar ,name
,(if body
`(let ((it (make-hash-table
:test ,test :size ,size :hash-function ,hash)))
,@(mapcar #'(lambda (p)
`(setf (gethash ,(car p) it) ,(cdr p)))
body)
it)
`(make-hash-table
:test ,test :size ,size :hash-function ,hash))))
(defun prime-failure-message (n name)
(concatenate
'string
(format nil "Dictionary ~A: " name)
(cond
((not (integerp n))
(format nil "~A of type ~A is not a number" n (type-of n)))
((< n 0)
(format nil "~A < 0; Dictionary sizes must > 0 and prime numbers" n))
((> n most-positive-fixnum)
(format nil "Dictionary sizes must be prime numbers < ~A"
most-positive-fixnum))
(t
(format nil "~A is not prime; Next prime larger than ~A is ~A"
n n (first (the-primes :from n :to (+ 1000 n))))))))
At _compile_ time
(define-dictionary (foo :size 20))
produces:
Error: Dictionary FOO: 20 is not prime; Next prime larger than 20 is 23
(define-dictionary (foo :size 23)
(1 . "one")
(2 . "two")
(3 . "three"))
Compiles, passes checks and produces the dictionary with the given entries.
This generates the following code:
(DEFVAR FOO
(LET ((IT (MAKE-HASH-TABLE
:TEST #<Function EQL>
:SIZE 23
:HASH-FUNCTION #<Function SXHASH>)))
(SETF (GETHASH 1 IT) "one")
(SETF (GETHASH 2 IT) "two")
(SETF (GETHASH 3 IT) "three")
IT))
/Jon