rhombus-prototype
rhombus-prototype copied to clipboard
Forbid breaking macro hygiene for safety
This proposal has a few parts:
- Non-hygienic macros are unsafe, and should not be allowed. Hygiene-breaking is strictly forbidden in the new system; "bindings as sets of scopes" is taken seriously in the same way that the scheme world tends to take lexical scope seriously. It should not be possible for a caller of a macro to unexpectedly exfiltrate information from the callee, for instance.
- I am interested in ocap-safety for macros that can be passed around safely between libraries. Admittedly I really do not fully understand what is necessary to achieve this goal, but my conversation with @erights has pointed me to the idea of doubly hygienic macros. I'm not quite sure that the "bindings as sets of scopes" approach in Racket, if actually taken seriously, wouldn't accomplish this.
- Finally, preventing user surprise. The link above discusses preventing programmer surprise in that the kinds of things a macro can do should be reflective of the kinds of things it looks like it might be able to do. That seems to indicate that Honu-style macros might not quite be safe enough; it's unclear to me that Honu macros can't do "surprising" things with code or that it's necessarily clear where they begin or end, and we should strive for maximum effort to prevent surprise to allow for user safety.
My suspicion is that while surface syntax may appear to be the most important goal of a new language, syntax safety is likely even more important for the direction computing is going.
+1. Syntax-parameters should be all you need on top of syntax-rules.
I love hygiene but I don't think we should be dogmatic about forcing every macro to be hygienic. We should allow unhygienic macros as long as macro implementer goes out of their way to specify it as unhygienic.
However, I think we could do a better job of specifying non-hygienic behavior, in documentation and possibly even contracts, although I'm not sure how that would work yet. One example is the struct macro. Its documentation specifies that on (struct name [field ...]) it defines:
struct:namenamename?name-field...
And if the documentation accounted for hygiene, it would specify that all of them are introduced with the same scopes as name. Without this information specified anywhere, another macro implementer couldn't properly expand to struct and know what they're getting.
As an alternative to banning unhygienic macros, we should consider only allowing unhygienic macros that specify exactly how they break hygiene. This could be a contract that specifies which identifiers they introduce and where they get the scopes for those identifiers.
Maybe something like this?
(define-syntax struct
(unhygienic/c
[(_ name:id [field:id ...])
#:defines
(list* (format-id #'name "struct:~a" #'name)
#'name
(format-id #'name "~a?" #'name)
(for/list ([field (in-list (attribute field))])
(format-id #'name "~a-~a" #'name field)))])
...actual-definition...)
Although I have no idea how unhygienic/c would be implemented or enforced. And perhaps there's a better way of specifying unhygienic behavior that's more concise or more declarative than what I have above.
I think that forcing hygiene is somewhat incompatible with Rhombus’ goal of being a language for creating other languages. In fact, it is impossible to completely remove non-hygienic macros while still implementing Racket in Rhombus for old code.