Live data from Hacker News

How Lisp macros differ from static code-generation and metaprogramming

brandonbyars.com

21–30 of 84 posts

Re: How Lisp macros differ from static code-generation and metaprogramming

#21
post #19

Earlier quoted context omitted.

1. Macros aren't unique to lisp. Perl6 also has macros ( http://en.wikipedia.org/wiki/Perl_6#Macros ) though they haven't been implemented in Rakudo yet. Also Ioke states that it has macros. 2. Metaprogramming's power ... The best example I know of is Io where pretty much anything other than commas and parentheses... Touched on Io introspection/metaprogramming before on HN: * http://news.ycombinator.com/item?id=18045…

The question isn't so much whether other languages have something they call "macros", but whether their macro systems approach Lisp's in power, flexibility, ease of use, integration with the language, and natural fit on to the language representation? Or is the macro system in question more of a Turing tarpit?

Prolog's macro system does, but given it's model of computation (search/pattern matching are built in), it ends up being used far less than Lisp's.

Re: How Lisp macros differ from static code-generation and metaprogramming

#22
post #9
post #2

Nice try blanco niño, but the following program runs fine. This doesn't detract from your overall point, but if you're trying to win over people who think the Boost macros are super awesome, this argument won't do it. #include #define LOOP(n) \ int i; \ for (i = 0; i #define LOOP10 \ LOOP(10) printf("%d\n", i) int main(int argc, char *argv[]) { LOOP10; }

Yesterday I was writing code that kept messing my Lisp's signal handling, so I wrote a FOR-DURATION macro that arms a timeout and makes sure whatever that runs in its body gets killed after N seconds. Here it is: (defmacro for-duration ((seconds) &body body) `(handler-case (bt:with-timeout (,seconds) ,@body) (bt:timeout () nil))) Five lines to alter the evaluation model of your language. Not bad. Use as: (for-duratio…

C99 + GCC extensions + POSIX:

  #define for_duration(seconds, body)                     \
    {                                                     \
      pthread_t tid_task, tid_watcher;                    \
                                                          \
      void* task(void* arg) {                             \
        body ;                                            \
        pthread_cancel(tid_watcher);                      \
        return NULL;                                      \
      }                                                   \
                                                          \
      void* watcher(void* arg) {                          \
        sleep((seconds));                                 \
        pthread_cancel(tid_task);                         \
        return NULL;                                      \
      }                                                   \
                                                          \
      pthread_create(&tid_task, NULL, &task, NULL);       \
      pthread_create(&tid_watcher, NULL, &watcher, NULL); \
      pthread_join(tid_task, NULL);                       \
    }                             
Use as:

  for_duration(10, {
      while (true) {
        printf("Infinite loop! where is your Godel now?\n");
      }
    });
  
Or with single brace-less statements:

  for_duration(10, while (true) printf("something\n") );
  
Lexical scope is sane:

  int i = 0;
  for_duration(10, {
      while (true) {
        printf("%d\n", i++);
      }
    });
My comparison isn't quite fair, because your threading library provides 'with-timeout', while pthreads doesn't. If you factored this out, say with a signature like:

  void with_timeout(unsigned int seconds, void (*func)(void));
(in analogy to the common lisp function), then the macro part becomes just four lines:

  #define for_duration(seconds, body) {			\
    void func() { body ; }				\
    with_timeout((seconds), &func);			\
  }
I acknowledge that the lisp solution is rather more elegant. Also, my C macro is potentially dangerous because it is unhygenic. (It shadows outer declarations of 'task()', 'watcher()', 'tid_task', 'tid_watcher', and 'arg', in the body of 'body'.)

Re: How Lisp macros differ from static code-generation and metaprogramming

#23
post #3

By the way, don't confuse macros with backquote; the backquote facility is probably closer to C's preprocessors. I haven't heard this claim before, and I might not be entirely right, but it's what provides for the "fill in the blanks" type symbolic computing. For example: (defun sum (x y) `(,x + ,y = ,(+ x y))) This is a SUM function which looks like this when run: (sum 4 5) ==> (4 + 5 = 9) The backquote (`) says the…

You can also splice values into backquoted expressions using ,@foo rather than ,foo - which I seem to remember being rather useful.

It's incredibly useful. The actual definition of until from Arc:

  (mac until (test . body)
    `(while (no ,test) ,@body))
It's really useful for bodies of code like this.

Re: How Lisp macros differ from static code-generation and metaprogramming

#24
post #17

> Except, of course, that doesn’t work. The preprocessor only makes one pass > through the file, meaning a macro can’t call another macro. Huh? That's just blatantly wrong, and I (and many others) have used the C preprocessor to create multi-level macros in quite powerful ways. See for example http://blog.nelhage.com/2010/07/implementing-an-edsl-in-cpp/ or the insanity Linux's tracing macros have implemented: http://…

The reason Linux trace macros are insane is because C preprocessor is so primitive.

Think about it.

> they are quite powerful

For small values of "quite". C macros are strictly less (and by large margin powerful than Lisp macros. One can't even use #ifdef inside #define!

Re: How Lisp macros differ from static code-generation and metaprogramming

#25
post #22
post #9

Earlier quoted context omitted.

Yesterday I was writing code that kept messing my Lisp's signal handling, so I wrote a FOR-DURATION macro that arms a timeout and makes sure whatever that runs in its body gets killed after N seconds. Here it is: (defmacro for-duration ((seconds) &body body) `(handler-case (bt:with-timeout (,seconds) ,@body) (bt:timeout () nil))) Five lines to alter the evaluation model of your language. Not bad. Use as: (for-duratio…

C99 + GCC extensions + POSIX: #define for_duration(seconds, body) \ { \ pthread_t tid_task, tid_watcher; \ \ void* task(void* arg) { \ body ; \ pthread_cancel(tid_watcher); \ return NULL; \ } \ \ void* watcher(void* arg) { \ sleep((seconds)); \ pthread_cancel(tid_task); \ return NULL; \ } \ \ pthread_create(&tid_task, NULL, &task, NULL); \ pthread_create(&tid_watcher, NULL, &watcher, NULL); \ pthread_join(tid_task, N…

I don't believe nested functions like you're using there are valid C. They're a nonstandard extension that some compilers implement because they're so handy. So this basically illustrates that C gets closer Lisp's usability when you add Lispy features to it.

Re: How Lisp macros differ from static code-generation and metaprogramming

#27
post #9

Earlier quoted context omitted.

Yesterday I was writing code that kept messing my Lisp's signal handling, so I wrote a FOR-DURATION macro that arms a timeout and makes sure whatever that runs in its body gets killed after N seconds. Here it is: (defmacro for-duration ((seconds) &body body) `(handler-case (bt:with-timeout (,seconds) ,@body) (bt:timeout () nil))) Five lines to alter the evaluation model of your language. Not bad. Use as: (for-duratio…

(defun for-duration (seconds body) (handler-case (bt:with-timeout seconds (body)) (bt:timeout () nil))) (for-duration 10 (lambda () (loop (print "Infinite loop! where is your Godel now?"))))

I think Greg is trying to say "why the hell do we even need macros for this"?

Re: How Lisp macros differ from static code-generation and metaprogramming

#28
post #25
post #22

Earlier quoted context omitted.

C99 + GCC extensions + POSIX: #define for_duration(seconds, body) \ { \ pthread_t tid_task, tid_watcher; \ \ void* task(void* arg) { \ body ; \ pthread_cancel(tid_watcher); \ return NULL; \ } \ \ void* watcher(void* arg) { \ sleep((seconds)); \ pthread_cancel(tid_task); \ return NULL; \ } \ \ pthread_create(&tid_task, NULL, &task, NULL); \ pthread_create(&tid_watcher, NULL, &watcher, NULL); \ pthread_join(tid_task, N…

I don't believe nested functions like you're using there are valid C. They're a nonstandard extension that some compilers implement because they're so handy. So this basically illustrates that C gets closer Lisp's usability when you add Lispy features to it.

You're right, it's not even valid C99. My mistake.

Re: How Lisp macros differ from static code-generation and metaprogramming

#29
post #6

A couple points on the metaprogramming space (these aren't about the article, but the article reminded me of them): 1. Macros aren't unique to lisp. They're most developed and used in the lisp family, but you don't need to have a homoiconic language to have macros. The Mirah programming language--statically typed Ruby on the JVM--has non-hygenic macros, I know there's been a number of efforts to add macros to coffees…

1. Yet, still to this day, Lisp remains one of the few languages with powerful macros that normal people cannot only use, but enjoy. Also the Scheme/Racket community actively researches how to make them more robust and easier to write (yes equally powerful macro systems do exist for other languages - however, ease of use is often sacrificed) 2. Io is great but the depth of it's runtime meta-programming facilities mak…

I don't disagree and I'm not trying to argue with the article or anything. I was just hoping to point people interested in the topic towards things that took me a while to realize/run across.

As for Io's efficiency, I think that's more about the implementation than in the language. Javascript (prototypal inheritance) and Smalltalk (message passing) aren't that far off and both have decent performance. I know Steve Dekorte was working on getting Io running in javascript in December but I don't think it's a high priority project for him.

Re: How Lisp macros differ from static code-generation and metaprogramming

#30
post #6

A couple points on the metaprogramming space (these aren't about the article, but the article reminded me of them): 1. Macros aren't unique to lisp. They're most developed and used in the lisp family, but you don't need to have a homoiconic language to have macros. The Mirah programming language--statically typed Ruby on the JVM--has non-hygenic macros, I know there's been a number of efforts to add macros to coffees…

Macros actually saw a great deal of use in the assembly-language space for as long as people were writing large programs directly in assembly. (Typically, the assemblers would let you define "pseudo-operations" which could appear in place of an actual opcode, and whose arguments were used to fill in templates.) In fact, one of the raps on Unix among "big iron" programmers was that its assembler was so primitive --- meaning, in particular, that it had no useful macro facilities.
Post reply on HN