-
-
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
ks
arg todef
is ignored. In something closer to the real code, this would be used withprocedure-reduce-keyword-arity
to 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.