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.

Reply via email to