Your pull request https://github.com/racket/racket/pull/1644
helps clarify the issue to me, and it looks like the right kind of solution. At Wed, 22 Mar 2017 16:50:20 -0700, Alex Knauth wrote: > 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 [email protected]. > To post to this group, send email to [email protected]. > To view this discussion on the web visit > https://groups.google.com/d/msgid/racket-dev/2F051005-C8F0-4225-9CE3-08E44FEE89 > C6%40knauth.org. > For more options, visit https://groups.google.com/d/optout. -- 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 [email protected]. To post to this group, send email to [email protected]. To view this discussion on the web visit https://groups.google.com/d/msgid/racket-dev/20170324190820.88D3A65007F%40mail-svr1.cs.utah.edu. For more options, visit https://groups.google.com/d/optout.
