[plt-scheme] Scheme behavior

From: Ryan Culpepper (ryanc at ccs.neu.edu)
Date: Tue Apr 7 17:56:19 EDT 2009

On Apr 6, 2009, at 11:22 PM, Jay Riddle wrote:

> In order to learn scheme and macros a little better I was attempting  
> to write a macro that would allow me make a slighty simpler stuct- 
> copy.  My version of the struct-copy would require one few  
> parameters.  This is sort of like what ocaml has using the 'with'  
> statement on a structure(?I am not sure I used the correct terms  
> there?).
> So instead of writting
>      (struct-copy my-struct some-mystruct (fieldN valueN))
> I would write
>     (with some-mystruct (fieldN valueN))
> Scheme wasn't too happy with some of my primitive attempts with  
> (define-syntax....) so I attempted with define-macro.
> I came up with this
> (define-macro with
>    (λ (s . body)
>      (list 'cond
>        (list (list 'myvar? s) (append (list 'struct-copy 'myvar s)  
> body))
>        (list (list 'myfun? s) (append (list 'struct-copy 'myfun s)  
> body))
>        (list (list 'myexp? s) (append (list 'struct-copy 'myexp s)  
> body))
>        '(else (raise "091-33 'with' not defined for struct type")))))
> The error I am getting is "struct-copy: accessor name not associated  
> with the given structure type in:..."
> In order to narrow down what I was doing wrong I did a simple test  
> case that should be simular to what my macro was doing and I noticed  
> that this code would fail to compile in drscheme.
> 1   (define (test e)
> 2     (cond
> 3       ((myvar? e) (struct-copy myvar e (name "tom")(isfunc #t)))
> 4       ((myfun? e) (struct-copy myfun e (name "tom")(isfunc #t)))
> 5       ((myexp? e) (struct-copy myexp e (name "tom")(isfunc #t)))
> 6       (else (raise 091-33 'with' not defined for struct type)))
> 7     )
> Basically the only structure that has the field 'isfunc' is the  
> myexp structure.  Is scheme doing some type checking or something?   
> Why does scheme care about line 3 and 4 at compile time?

It's not type checking, but 'struct-copy' does require that the fields  
belong to the struct name, and that is checked at compile time. It  
does that because the names of the fields aren't actually remembered  
by the struct type at run time.

There's not really a good way to make 'struct-copy' more dynamic, but  
here's a hack that will let you get by for now:

;; (with-dynamic-syntax-error expr)
;; Catches syntax errors in 'expr' and delays them until run time.
(define-syntax (with-dynamic-syntax-error stx)
   (if (eq? (syntax-local-context) 'expression)
       (syntax-case stx ()
         [(with-dynamic-syntax-error expr)
          (with-handlers ([exn:fail?
                           (lambda (exn)
                             (with-syntax ([msg (exn-message exn)])
                               (syntax/loc #'expr (error 'msg))))])
            (local-expand #'expr 'expression null))])
       #`(#%expression #,stx)))

Now you can produce bad uses of 'struct-copy', as long as you wrap  
them in the macro above. Here's 'with' rewritten:

(define-syntax (with stx)
   (syntax-case stx ()
     [(with struct-expr (field value) ...)
      (with-syntax ([(tmp ...) (generate-temporaries #'(field ...))])
        #'(let ([x struct-expr]
                [tmp value] ...)
            (cond [(myvar? x)
                   (with-dynamic-syntax-error (struct-copy myvar x  
(field tmp) ...))]
                  [(myexp? x)
                   (with-dynamic-syntax-error (struct-copy myexp x  
(field tmp) ...))]
                  [(myfun? x)
                   (with-dynamic-syntax-error (struct-copy myfun x  
(field tmp) ...))]
                  [else (raise-type-error 'with "myvar, myexp, or  
myfun" x)])))]))


Posted on the users mailing list.