[plt-scheme] On hygiene and trust

From: Joe Marshall (jmarshall at alum.mit.edu)
Date: Thu Jul 9 13:24:08 EDT 2009

On Wed, Jul 8, 2009 at 5:41 PM, Abdulaziz Ghuloum<aghuloum at gmail.com> wrote:
>
> On Jul 8, 2009, at 6:04 PM, Joe Marshall wrote:
>
>> On Wed, Jul 8, 2009 at 7:31 AM, Abdulaziz Ghuloum<aghuloum at gmail.com>
>> wrote:
>>>
>>> I don't agree.  Show me a painful simple use.  Pick any simple
>>> macro you want: let, let*, or, and, cond, case, or any other
>>> macro of your choice to show the pain.
>>
>> This came up the other day.  Transform something like this:
>>
>> (define-event foo bar (arg1 arg2 ...)
>>   (form1)
>>   (form2 ...) etc.)
>>
>> into something like this:
>>
>> (define (foo$bar arg1 arg2 ...)
>>   (form1)
>>   (form2 ...) etc.)
>
> I don't see the pain caused by "syntax-case", and maybe I don't
> understand the problem you're having.  Can you please write your
> macro in syntax-case because my attempt below probably does not
> do what you had in mind.
>
> (define-syntax define-event
>  (lambda (stx)
>    (define (concat x y) --- fill in the blank ---)
>    (syntax-case stx ()
>      [(_ foo bar (arg1 arg2 ...)
>          (form1)
>          (form2 ...) etc.)
>       #`(define (#,(concat #'foo #'bar) arg1 arg2 ...)
>           (form1)
>           (form2 ...) etc.)])))

(define-syntax define-event
 (lambda (stx)
   (define (concat . x)
    (string->symbol (apply string-append (map symbol->string x))))
   (syntax-case stx ()
     [(_ foo bar arguments form1 . body)
      #`(define (#,(concat #'foo '$ #'bar) . arguments)
          form1 . body)])))

(define-event foo bar (x y) (list x y))
collects\scheme\private\map.ss:22:17: symbol->string: expects argument
of type <symbol>; given #<syntax:3:16>

This is what usually happens when I try to use syntax-case.  I end up getting
syntax objects where I want symbols, or symbols where I want syntax objects,
or syntax object closed in the wrong scope.  Then I end up trying permutations
of sharps, backticks, commas, and quotes until it eventually works.

Yes, it is laziness on my part.  I ought to re-read the documentation, and if I
had practice at it, I'd be ok, but I don't write all that many macros, so when
I do, I inevitably have to re-learn it.

>
>
>>>> defmacro is really easy to use and understand, but isn't hygienic.
>>>
>>> I don't agree that defmacro is easy to use without a pattern
>>> matching facility.  Again, write the example that you provide
>>> (above) using defmacro; I doubt it will be simpler or easier
>>> or more robust, or anything.
>>
>> Do you consider backquote a pattern matching facility?
>
> Not really.
>
>> ;; Common lisp version of named-let
>> (defmacro named-let (name bindings &body body)
>>  `(LABELS ((,name ,(map 'list #'car bindings) , at body))
>>     (,name ,@(map 'list #'cadr bindings))))
>
> This code has a bug that's not related to hygiene but to the scope
> of the "name" identifier.

Yeah.  it should be more like this:
(defmacro named-let (name bindings &body body)
  `(funcall (LABELS ((,name ,(map list #'car bindings) , at body))
                 (function name)) ,@(map 'list #'cadr bindings)))

But the compiler generates much worse code for this.  The scoping
issue is a minor issue.

>  But either way, I don't see that as simpler
> or easier to use than, say, the following (exhibiting the same bug):
>
> (define-syntax-rule
>  (named-let name ((lhs* rhs*) ...) b b* ...)
>  (letrec ((name (lambda (lhs* ...) b b* ...)))
>    (name rhs* ...)))
>
> It looks like named-let is a trivial macro that can be handled
> trivially in any macro system, so, it does not really show that
> "defmacro is really easy to use and understand".  As the complexity
> of the macro increases, doesn't it just cause more littering with
> all of these map this and map that and splice this and that?

Yes, but I don't think it is any more complex than the ellipses.

> PS. I'm just trying to understand your position, since I was pretty
> surprised that *you* find defmacro style superior to anything else
> in scheme (modulo hygiene as you said).

I didn't say that.  I said it was easier to understand what the defmacro
form is doing to the code.  The transformation from list-structure to
different list-structure is pretty straightforward.  Syntax objects are
more opaque:  #<syntax:3:16>

What is that?  An identifier?  Which one?  (A quote from the King James
Bible, book of Syntax, Chapter 3, verse 16?)

I can't `mapcar' over a syntax object.

And when I go in the other direction, I have the symbol `foo' and I wish
to create some code that uses it, I have to transform it into a syntax
object with the appropriate scope.

>  I mean, you know macros,
> and none of this is new to you, but do you really find manipulating
> S-expressions that much easier and simpler than manipulating syntax
> objects?  Or is it something else?

I do find list structure easier to manipulate that syntax objects.  Syntax
objects *include* everything that is in the list structure, and then add some
more stuff.  Furthermore, syntax objects don't have the incredibly rich
library of list manipulation functions.

That said, I don't *prefer* defmacro style macros.  They are buggy, and
while it is obvious to see *what* they do to the code, it isn't obvious how
to make them work correctly.

On the other hand, I don't much like syntax-case.  It is much more
complicated to use, and I see it used too often.

I want something else.  I'm not sure what, though.

-- 
~jrm


Posted on the users mailing list.