• Re: Winstond and Horn Macros

    From B. Pym@21:1/5 to All on Thu Jul 4 18:32:11 2024
    Doug Tolton wrote:
    Problem 12-4 is:
    Suppose, for some immensely peculiar reason, you want a form of LET
    that does not evaluate initial value forms. Define LETQ such that
    LETQ arranges the following translation:
    (letq ((<variable1> <value1>)
    (<variable2> <value2>)
    ...)
    <body> )

    translate to:

    (let ((<variable1> '<value1>)
    (<variable2> '<value2>)
    ...)
    <body> )

    [ snip ]

    I
    could just be missing something simple, but I don't understand how I
    would take an arbitrary number of variables, and map only value
    expressions so they aren't evaluated. Is there something simple I'm missing? Or is there an entire conceptual idea I'm not getting?

    A macro is just a Lisp function that turns the list (LETQ ...) to (LET
    ...) according to the above schema. Write a function that does this,
    and then turn it into a macro.

    You might be confused by the DEFMACRO utility, which has built-in destructuring. Prior to things like DEFMACRO, Lisp macros were written
    as ordinary functions that worked on the entire form. A LETQ macro
    would receive just one argument: the entire source code of the macro
    call: (LETQ ...)

    With DEFMACRO, you have some built-in analysis. The form is matched
    against a syntactic pattern, and pieces are assigned to variables.
    (You can still receive the entire form in one piece using the &whole
    lambda keyword).

    With the preprocessing that DEFMACRO performs, it's no longer as
    obvious to newbies that the macro is a function that turns one form
    into another.

    When there are repeated units to be handled, like ((<variable> <value>
    ...)) lists, newbies get confused, because they can't find the right
    DEFMACRO syntax which would nicely destructure it without any
    expenditure of programming work. ;)

    The answer is that where DEFMACRO's destructuring runs out of steam,
    you have to switch to ``manual'' list processing. For example, you can capture the entire ((<variable> <value>) ...) sublist, attaching it to
    one variable:

    (defmacro letq ((&rest variable-value-list) &body forms)
    `(let ,(transform-variable-value-list variable-value-list)
    ,@forms))

    Now you just need to write the function TRANSFORM-VARIABLE-VALUE-LIST.
    This can be done using

    (loop for (variable value) in variable-value-list
    collecting `(,variable (quote ,value)))

    so the whole macro becomes:

    (defmacro letq ((&rest variable-value-list) &body forms)
    (let ((transformed-list (loop for (variable value)
    in variable-value-list
    collecting `(,variable ',value))))
    `(let ,transformed-list ,@forms)))

    In other words, the function is nice and short so we roll it into the
    body of the macro, thus sparing ourselves a lesson about eval-when. ;)


    Let's check the output under SBCL:

    * (letq ((a aa) (b bb)) (list a b))
    ===>
    (AA BB)

    Scheme

    (define-syntax letq
    (syntax-rules ()
    [(_ ((v s) ...) expr ...)
    (let ((v 's) ...) expr ...)]))

    (letq ((a aaa) (b bbb) (c ccc)) (list a b c))
    ===>
    (aaa bbb ccc)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)