Live data from Hacker News

Lisp Macros, Delayed Evaluation and the Evolution of Smalltalk

blog.metaobject.com

1–10 of 114 posts

Re: Lisp Macros, Delayed Evaluation and the Evolution of Smalltalk

#2
That's a common misconception that Lisp macros are mostly used to 'delay' evaluation.

What Smalltalk calls 'blocks' are just (anonymous) functions in Lisp. Books like SICP explain in detail how to use that for delayed evaluation in Lisp/Scheme:

https://mitpress.mit.edu/sites/default/files/sicp/full-text/...

Re: Lisp Macros, Delayed Evaluation and the Evolution of Smalltalk

#3
post #2

That's a common misconception that Lisp macros are mostly used to 'delay' evaluation. What Smalltalk calls 'blocks' are just (anonymous) functions in Lisp. Books like SICP explain in detail how to use that for delayed evaluation in Lisp/Scheme: https://mitpress.mit.edu/sites/default/files/sicp/full-text/...

Smalltalk blocks are anonymous functions sure, but the evaluation is delayed - you have to pass the message #value (and its variants) to it. This not the case in Scheme and maybe Lisps, where evaluation is eager.

Passing Smalltalk blocks and Scheme/Lisp lambdas into functions have different behaviours.

Re: Lisp Macros, Delayed Evaluation and the Evolution of Smalltalk

#4
post #2

That's a common misconception that Lisp macros are mostly used to 'delay' evaluation. What Smalltalk calls 'blocks' are just (anonymous) functions in Lisp. Books like SICP explain in detail how to use that for delayed evaluation in Lisp/Scheme: https://mitpress.mit.edu/sites/default/files/sicp/full-text/...

Smalltalk blocks are anonymous functions sure, but the evaluation is delayed - you have to pass the message #value (and its variants) to it. This not the case in Scheme and maybe Lisps, where evaluation is eager. Passing Smalltalk blocks and Scheme/Lisp lambdas into functions have different behaviours.

That doesn't matter. One can write IF as a function taking functions as THEN and ELSE forms:

    CL-USER 9 > (defun my-if (test then else)
                  (if test (funcall then) (funcall else)))
    MY-IF

    CL-USER 10 > (let ((a 10))
                  (my-if (> a 5)
                         (lambda ()
                           (print 'hello))
                         (lambda ()
                           (print 'world)))
                  'done)

    HELLO 
    DONE
The lambdas you see above serve the same purpose as blocks in Smalltalk.

Re: Lisp Macros, Delayed Evaluation and the Evolution of Smalltalk

#5
post #2

That's a common misconception that Lisp macros are mostly used to 'delay' evaluation. What Smalltalk calls 'blocks' are just (anonymous) functions in Lisp. Books like SICP explain in detail how to use that for delayed evaluation in Lisp/Scheme: https://mitpress.mit.edu/sites/default/files/sicp/full-text/...

What are lisp macros for if not to delay evaluation? I assume you're making a distinction between purpose and mechanism here?

I've always understood the semantics to be:

fn: (a b c) => (call a (eval b) (eval c))

mac: (a b c) => (eval (call a b c))

Re: Lisp Macros, Delayed Evaluation and the Evolution of Smalltalk

#6
post #2

That's a common misconception that Lisp macros are mostly used to 'delay' evaluation. What Smalltalk calls 'blocks' are just (anonymous) functions in Lisp. Books like SICP explain in detail how to use that for delayed evaluation in Lisp/Scheme: https://mitpress.mit.edu/sites/default/files/sicp/full-text/...

What are lisp macros for if not to delay evaluation? I assume you're making a distinction between purpose and mechanism here? I've always understood the semantics to be: fn: (a b c) => (call a (eval b) (eval c)) mac: (a b c) => (eval (call a b c))

That might be more like what was called an FEXPR mechanism in some early Lisps or some niche Lisps, or even in languages like R.

https://en.wikipedia.org/wiki/Fexpr

When an FEXPR is called, it gets unevaluated args and can then at runtime decide what to do.

Macros OTOH are a different mechanism, where expressions get rewritten at macro expansion time, which can be for example at compile time. The macro then gets called with arbitrary expressions it encloses (those don't need to be valid code by themselves) and computes new source.

Thus macros are code generators from expressions. In a compiled implementation, macro expansion is done with compilation interleaved: each form gets expanded, even repeatedly until it no longer expands into a macro, and then the resulting non-macro form gets compiled.

Thus in a way a macro does not delay execution, it does the opposite: it actually shifts computation to compile time -> the computation of code from expressions and the computation of arbitrary side effects in the compile-time environment.

In an interpreter version of Lisp, the macro gets also expanded at runtime - but in its own macroexpansion during evaluation. There eval will call the macroexpander repeatedly until it gets a non-macro form.

Now, what can you do with arbitrary complex code generators at compile time? https://stackoverflow.com/a/2563308/69545

Actually Paul Graham wrote a classical Lisp book explaining a bunch of things around macros. Available here for download:

http://www.paulgraham.com/onlisp.html

The classic Lisp article which motivated the move from FEXPRs to macros:

https://www.nhplace.com/kent/Papers/Special-Forms.html

Re: Lisp Macros, Delayed Evaluation and the Evolution of Smalltalk

#7
post #2

That's a common misconception that Lisp macros are mostly used to 'delay' evaluation. What Smalltalk calls 'blocks' are just (anonymous) functions in Lisp. Books like SICP explain in detail how to use that for delayed evaluation in Lisp/Scheme: https://mitpress.mit.edu/sites/default/files/sicp/full-text/...

What are lisp macros for if not to delay evaluation? I assume you're making a distinction between purpose and mechanism here? I've always understood the semantics to be: fn: (a b c) => (call a (eval b) (eval c)) mac: (a b c) => (eval (call a b c))

Take a macro like (infix x + vx * dt). It's not equivalent to calling some function with thunked arguments -- it has to analyze the literal text of the arguments to turn this into (+ x (* vx dt)).

Re: Lisp Macros, Delayed Evaluation and the Evolution of Smalltalk

#8
post #2

That's a common misconception that Lisp macros are mostly used to 'delay' evaluation. What Smalltalk calls 'blocks' are just (anonymous) functions in Lisp. Books like SICP explain in detail how to use that for delayed evaluation in Lisp/Scheme: https://mitpress.mit.edu/sites/default/files/sicp/full-text/...

What are lisp macros for if not to delay evaluation? I assume you're making a distinction between purpose and mechanism here? I've always understood the semantics to be: fn: (a b c) => (call a (eval b) (eval c)) mac: (a b c) => (eval (call a b c))

i'll defer to lispm's expert opinion, but i always thought of macros as _conditional_ evaluation. `if` is a special form because it does not evaluate the branch not taken. it's tricky to implement `if` without using `if` or `cond`. macros make it easy to implement your own special forms. in your example, you might want to implement `first`.

    (first b c) -> (eval b)
i can't think of a way (but it might be possible) to not evaluate c with a regular function. first is trivial and dumb, but it's obvious how to turn that into short circuiting `and` or `or`.

Re: Lisp Macros, Delayed Evaluation and the Evolution of Smalltalk

#9
post #4

Earlier quoted context omitted.

Smalltalk blocks are anonymous functions sure, but the evaluation is delayed - you have to pass the message #value (and its variants) to it. This not the case in Scheme and maybe Lisps, where evaluation is eager. Passing Smalltalk blocks and Scheme/Lisp lambdas into functions have different behaviours.

That doesn't matter. One can write IF as a function taking functions as THEN and ELSE forms: CL-USER 9 > (defun my-if (test then else) (if test (funcall then) (funcall else))) MY-IF CL-USER 10 > (let ((a 10)) (my-if (> a 5) (lambda () (print 'hello)) (lambda () (print 'world))) 'done) HELLO DONE The lambdas you see above serve the same purpose as blocks in Smalltalk.

It works, but the syntax isn't nice enough to use as-is, so in practice you need a macro.

The innovation here is using better syntax for anonymous functions to make such things practical in languages without macros.

Re: Lisp Macros, Delayed Evaluation and the Evolution of Smalltalk

#10
post #2

That's a common misconception that Lisp macros are mostly used to 'delay' evaluation. What Smalltalk calls 'blocks' are just (anonymous) functions in Lisp. Books like SICP explain in detail how to use that for delayed evaluation in Lisp/Scheme: https://mitpress.mit.edu/sites/default/files/sicp/full-text/...

What are lisp macros for if not to delay evaluation? I assume you're making a distinction between purpose and mechanism here? I've always understood the semantics to be: fn: (a b c) => (call a (eval b) (eval c)) mac: (a b c) => (eval (call a b c))

Macros determine what requirements apply to the processing of syntax. Which parts of that syntax, if any, are evaluated according to what rules, in what environment.
Post reply on HN