I'm having trouble writing macros that expand to uses of generics properly. Depending on what I do in different instances it either "silently" fails to implement the method or results in an ambiguous binding error.
(My end goal with this is to wrap macros around generics to do type-checking on those parts of the program, using the method of Type Systems as Macros.) The very simplest version of my problem is a macro that uses gen:custom-write in it's own scope: #lang agile (define-simple-macro (my-struct name [field ...] #:custom-write [method-def ...]) (struct name [field ...] #:methods gen:custom-write [method-def ...])) (my-struct foo [a] #:custom-write [(define (write-proc this out mode) (fprintf out "~v" (foo-a this)))]) The problem here is that the struct form assumes that the method definitions will be in the same lexical context as the use of gen:custom-write. This can be solved by using `datum->syntax` or `syntax-local-introduce`. That makes it unhygienic, but that's fine so far. The next version of my problem is a macro that takes its own notion of a generic interface and needs to expand into racket's generics. #lang agile (begin-for-syntax (struct interface-info [internal-id])) (define-syntax my-custom-write (interface-info #'gen:custom-write)) ;; my structs specify generics using the #:my-methods keyword, ;; which expects an identifier bound with define-syntax to an ;; interface-info struct (define-simple-macro (my-struct name [field ...] #:my-methods gen [method-def ...]) #:with internal-id (interface-info-internal-id (syntax-local-value #'gen)) (struct name [field ...] #:methods internal-id [method-def ...])) (my-struct foo [a] #:my-methods my-custom-write [(define (write-proc this out mode) (fprintf out "~v" (foo-a this)))]) This has the same problem, but with an added indirection. The actual `gen:custom-write` identifier comes from someplace else, the innards of `my-custom-write`. Because of this I can't just `datum->syntax` it or `syntax-local-introduce` it, it could be imported from another file! ;; interface-info.rkt #lang agile (begin-for-syntax (provide (struct-out interface-info)) (struct interface-info [internal-id])) ;; my-custom-write.rkt #lang agile (provide my-custom-write) (require "interface-info.rkt") (define-syntax my-custom-write (interface-info #'gen:custom-write)) ; gen:custom-write is bound in this scope With this, using `syntax-local-introduce` on the internal id has the same problem of failing to implement the method: (define-simple-macro (my-struct name [field ...] #:my-methods gen [method-def ...]) #:with internal-id (syntax-local-introduce (interface-info-internal-id (syntax-local-value #'gen))) (struct name [field ...] #:methods internal-id [method-def ...])) And trying to use `make-syntax-delta-introducer` results in an ambiguous binding error: (define-simple-macro (my-struct name [field ...] #:my-methods gen [method-def ...]) #:with internal-id ((make-syntax-delta-introducer #'gen #false) (interface-info-internal-id (syntax-local-value #'gen))) (struct name [field ...] #:methods internal-id [method-def ...])) And using two deltas to remove the internal-id scopes and add the gen scopes seems to work at first: (define-simple-macro (my-struct name [field ...] #:my-methods gen [method-def ...]) #:with internal-id (interface-info-internal-id (syntax-local-value #'gen)) #:with internal-id* ((make-syntax-delta-introducer #'gen #false) ((make-syntax-delta-introducer #'internal-id #false) #'internal-id 'remove) 'add) (struct name [field ...] #:methods internal-id [method-def ...])) Until you realize that completely destroyed the original binding of the internal-id so that it fails on a simple example like: (define gen:custom-write "I wouldn't attempt to interfere with bindings within other files") (my-struct foo [a] #:my-methods my-custom-write [(define (write-proc this out mode) (fprintf out "~v" (foo-a this)))]) ;. my-custom-write.rkt:5:20: struct: the first argument to the #:methods specification is not a name for a generic interface in: gen:custom-write Without destroying the binding of the internal-id to gen:custom-write from the other file, how do I do this properly? Is the ambiguous binding error a sign that I'm getting closer? Is there any other way to introduce generic interfaces that would be more friendly to macros? (By the way, my last example of defining a new gen:custom-write is not contrived. It is necessary because the new definition is the typed version.) Alex Knauth -- You received this message because you are subscribed to the Google Groups "Racket Developers" group. To unsubscribe from this group and stop receiving emails from it, send an email to racket-dev+unsubscr...@googlegroups.com. To post to this group, send email to racket-dev@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/racket-dev/2F051005-C8F0-4225-9CE3-08E44FEE89C6%40knauth.org. For more options, visit https://groups.google.com/d/optout.