I have a macro that does something that approximates binding. It looks like this:
(∀ [α] (→ α α)) Obviously, I’d really like it if the αs had binding arrows drawn to them. The trouble, unfortunately, is that ∀ does not expand to a binding form at all; it is parsed in a single macro step to a value composed of prefab structs, which is then stashed in a preserved syntax property somewhere. No identifier with the name α is ever bound. It seems like the 'disappeared-binding and 'disappeared-use properties are for precisely this purpose, but in my initial implementation, I found that they had some problems for this use case. To illustrate, consider the following macro: (define-syntax-parser bind [(_ binder:id use:id) (syntax-properties #'(void) (list (cons 'disappeared-binding (syntax-local-introduce #'binder)) (cons 'disappeared-use (syntax-local-introduce #'use))))]) (Where syntax-properties is a simple function that attaches multiple syntax properties from a dictionary.) This works okay, since I can now do this: (bind α α) …and DrRacket draws a binding arrow between the two αs. However, there is a problem. If I do this: (bind α α) (bind α α) …then DrRacket draws binding arrows between all the αs, not just each pair, since they are all free-identifier=?. This is somewhat problematic, since I don’t ever actually bind α, and the identifiers will almost always be free-identifier=? if they have the same name. What I really want is a way to draw arrows very deliberately. I want a way to say “draw an arrow between these two identifiers, but not any other ones”. Unfortunately, all of the syntax properties available ('disappeared-use, 'disappeared-binding, and 'sub-range-binders) depend on some inspection of identifier bindings, as far as I can tell. To solve this problem, I found a way to cheat: I can forge identifiers with made-up source locations to get Check Syntax to do what I want: (define (binding-arrows-props binders uses) (define tmp (generate-temporary)) (define (mk-tmp id) (propagate-original-for-check-syntax id (datum->syntax tmp (syntax-e tmp) id id))) (list (cons 'disappeared-binding (map mk-tmp binders)) (cons 'disappeared-use (map mk-tmp uses)))) (define (binding-arrows stx binders uses) (syntax-properties stx (binding-arrows-props binders uses))) (Where propagate-original-for-check-syntax inspects a syntax object and applies the 'original-for-check-syntax syntax property if necessary.) Using these functions, I can invent identifiers that will “trick” Check Syntax into drawing binding arrows between any sets of identifiers that I desire. In fact, the endpoints of the arrows don’t even need to be identifiers! I can do this: (define-syntax-parser bind [(_ a b) (binding-arrows #'(void) (list (syntax-local-introduce #'a)) (list (syntax-local-introduce #'b)))]) …and Check Syntax will now happily draw arrows between absolutely anything at all: (bind α α) (bind #s(point 1 2) 75) This is kind of neat, but it seems like a horrible hack. Is there a better way to get the arrows I want even if my macro is a little bit naughty and simply emulates binding even when there isn’t any? Are there any terrible pitfalls with the approach I’ve taken? For what it’s worth, this isn’t hypothetical — this is an actual issue I’ve run into, and this has been my kludge of a solution. If everyone thinks this is a-ok, I’ll keep using it, but if there’s a better way, I’d happily switch to something nicer. (Also, as a final note, my apologies for asking so many questions about the Check Syntax arrows on this list, but they’re really, really useful, and I try my best to cooperate with them whenever I can.) -- You received this message because you are subscribed to the Google Groups "Racket Users" group. To unsubscribe from this group and stop receiving emails from it, send an email to racket-users+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.