-
-
Save greghendershott/4039102 to your computer and use it in GitHub Desktop.
| #lang racket | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;; | |
| ;; Scenario 1: Plain function | |
| ;; | |
| (define (def-proc ks) | |
| ;; Proc that takes a dict? | |
| (define (f/dict d) | |
| (displayln d)) | |
| ;; Wrap f/dict in a proc that takes keyword arguments, instead. | |
| (define symbol->keyword (compose1 string->keyword symbol->string)) | |
| (define keyword->symbol (compose1 string->symbol keyword->string)) | |
| (define (keyword<=? a b) (string<=? (keyword->string a) (keyword->string b))) | |
| (define f/kw (make-keyword-procedure | |
| (lambda (kws vs . rest) | |
| (f/dict (map cons | |
| (map keyword->symbol kws) | |
| vs))))) | |
| f/kw) | |
| (define foo (def-proc '(a b c))) | |
| (foo #:a 1 #:b 2 #:c 3) | |
| ;; => | |
| ;; ((a . 1) (b . 2) (c . 3)) | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;; | |
| ;; Scenario 2: Using a macro | |
| ;; | |
| (begin-for-syntax | |
| (define (def-proc ks) ;; This is EXACTLY the same as above | |
| ;; Proc that takes a dict? | |
| (define (f/dict d) | |
| (displayln d)) | |
| ;; Wrap f/dict in a proc that takes keyword arguments | |
| (define symbol->keyword (compose1 string->keyword symbol->string)) | |
| (define keyword->symbol (compose1 string->symbol keyword->string)) | |
| (define (keyword<=? a b) (string<=? (keyword->string a) (keyword->string b))) | |
| (define f/kw (make-keyword-procedure | |
| (lambda (kws vs . rest) | |
| (f/dict (map cons | |
| (map keyword->symbol kws) | |
| vs))))) | |
| f/kw)) | |
| (define-syntax (def stx) | |
| (syntax-case stx () | |
| [(_ name keys) | |
| (let ([ks (syntax->list #'keys)]) | |
| #`(begin | |
| (define name #,(def-proc ks))))])) | |
| (def foo2 '(a b c)) | |
| (foo2 #:a 1 #:b 2 #:c 3) | |
| ;; => | |
| ;; application: procedure does not accept keyword arguments | |
| ;; procedure: ...t/private/kw.rkt:191:14 | |
| ;; given arguments: | |
| ;; #:a 1 | |
| ;; #:b 2 | |
| ;; #:c 3 | |
| ;; Huh ???? |
The problem is phase-crossing. You're accidentally creating 3-d syntax, and the struct that handles kw arguments at phase 1 is different from the one that handles kw arguments at phase 0.
But really, the main problem is that you're macro is using the return value of (def-proc ks) in the result syntax, even though def-proc returns a procedure, not syntax.
Thanks!
The problem is phase-crossing. You're accidentally creating 3-d syntax, and the struct that handles kw arguments at phase 1 is different from the one that handles kw arguments at phase 0.
I'd intuited it had something to do with phases, but I was having a hard time seeing specifically how.
you're macro is using the return value of (def-proc ks) in the result syntax, even though def-proc returns a procedure, not syntax
Ah right. That's actually a artifact of distilling it down to an example (my "real" def-proc did return syntax).
@samth what I ended up with is here: https://github.com/greghendershott/gapi/blob/c5e3e844c441623030c9af4307e3a03fa4321328/macro.rkt#L49
It works, but creating the syntax for a struct literal seems much more complicated than I expected. I have the feeling I went down the wrong fork in the road.
(BTW the background for this is that I had a lot of nearly-duplicated code in GAPI lib for using it in a dynamic/runtime way vs. a compiled/macro way. I wanted to refactor it.)
I realize the
ksarg todefis ignored. In something closer to the real code, this would be used withprocedure-reduce-keyword-arityto create a procedure that accepts only those specific keyword arguments. But I trimmed all that out of this example because that additional element isn't needed to demonstrate the problem.