{
"path": "/blog/2013-09-27-hygiene-in-sweetjs",
"site": "at://did:plc:4vjd3fe2cgzq5d24j4f3zvar/site.standard.publication/3mjz2bzni752x",
"$type": "site.standard.document",
"title": "Hygiene in sweetjs",
"content": {
"$type": "dev.disnet.blog.content.markdown",
"markdown": "\nThe most important feature of [sweet.js](http://sweetjs.org) is hygiene. Hygiene prevents variables names inside of macros from clashing with variables in the surrounding code. It's what gives macros the power to actually be syntactic abstractions by hiding implementation details and allowing you to use a hygienic macro *anywhere* in your code.\n\nFor hygiene to work sweet.js must rename variables. Recently several people have asked me why sweet.js renames **all** the variables. Wouldn't it be better and cleaner to only rename the variables that macros introduce?\n\nThe tl;dr is \"because hygiene\" but let's unpack that a little.\n\n## Hygiene Part 1 (Binding) ##\n\nThe part of hygiene most people intuitively grok is keeping track of the variable *bindings* that a macro introduces. For example, the swap macro creates a `tmp` variable that should only be bound inside of the macro:\n\n```javascript\nmacro swap {\n rule { ($a, $b) } => {\n var tmp = $a;\n $a = $b;\n $b = tmp;\n }\n}\n\nvar tmp = 10;\nvar b = 20;\nswap (tmp, b)\n```\n\nHygiene keeps the two `tmp` bindings distinct by renaming them both:\n\n```js\nvar tmp$1 = 10;\nvar b$2 = 20;\n\nvar tmp$3 = tmp$1;\ntmp$1 = b$2;\nb$2 = tmp$3;\n```\n\nThis is the point where most people say \"why bother renaming the variables outside of the macro\"? Can't you just rename the bindings created by the macro? Wouldn't it be cleaner for the expansion to just be something like:\n\n```js\nvar tmp = 10;\nvar b = 20;\n\nvar tmp$1 = tmp;\ntmp = b;\nb = tmp$1;\n```\n\n## Hygiene Part 2 (Reference) ##\n\nThe complication comes in with variable *references*. The body of a macro can contain references to bindings declared outside of the macro and those references must be consistent no matter the context in which the macro is invoked.\n\nSome code to clarify. Let's say you have a macro that uses a random number function:\n\n```js\nvar random = function(seed) { /* ... */ }\nlet m = macro {\n rule {()} => {\n var n = random(42);\n // ...\n }\n}\n```\n\nThis macro needs to refer to `random` in any context that it gets invoked. But its context could have a different binding to `random`!\n\n```js\nfunction foo() {\n var random = 42;\n m ()\n}\n```\n\nHygiene needs to keep the two `random` bindings different. So sweet.js will expand this into something like:\n\n```js\nvar random$1 = function(seed$4) { /* ... */ }\nfunction foo() {\n var random$2 = 42;\n var n$3 = random$1(42);\n // ...\n}\n```\n\nNote that there is no way for hygiene to do this if it only renamed identifiers inside of macros since both `random` bindings were declared outside of the macro. Hygiene is necessarily a whole program transformation.\n\n(ps if this sort of feels like a closure you're on to something: one of the early techniques that led to modern hygiene algorithms was called [syntactic closures](ftp://publications.ai.mit.edu/ai-publications/pdf/AIM-1049.pdf))\n\nStrictly speaking the hygiene algorithm is still conservative. Variable bindings declared outside of a macro that are never referenced by a macro don't really need to be renamed. However, modifying the hygiene algorithm to only rename *exactly* what needs to be renamed seems pretty difficult (especially to do so efficiently). If anyone knows techniques for this definitely let me know (or even better submit a pull request).\n",
"sourceFormat": "markdown"
},
"publishedAt": "2013-09-27T00:00:00.000Z",
"textContent": "The most important feature of sweet.js is hygiene. Hygiene prevents variables names inside of macros from clashing with variables in the surrounding code. It's what gives macros the power to actually be syntactic abstractions by hiding implementation details and allowing you to use a hygienic macro anywhere in your code. For hygiene to work sweet.js must rename variables. Recently several people have asked me why sweet.js renames all the variables. Wouldn't it be better and cleaner to only rename the variables that macros introduce? The tl;dr is \"because hygiene\" but let's unpack that a little. Hygiene Part 1 (Binding) The part of hygiene most people intuitively grok is keeping track of the variable bindings that a macro introduces. For example, the swap macro creates a tmp variable that should only be bound inside of the macro: Hygiene keeps the two tmp bindings distinct by renaming them both: This is the point where most people say \"why bother renaming the variables outside of the macro\"? Can't you just rename the bindings created by the macro? Wouldn't it be cleaner for the expansion to just be something like: Hygiene Part 2 (Reference) The complication comes in with variable references . The body of a macro can contain references to bindings declared outside of the macro and those references must be consistent no matter the context in which the macro is invoked. Some code to clarify. Let's say you have a macro that uses a random number function: This macro needs to refer to random in any context that it gets invoked. But its context could have a different binding to random! Hygiene needs to keep the two random bindings different. So sweet.js will expand this into something like: Note that there is no way for hygiene to do this if it only renamed identifiers inside of macros since both random bindings were declared outside of the macro. Hygiene is necessarily a whole program transformation. (ps if this sort of feels like a closure you're on to something: one of the early techniques that led to modern hygiene algorithms was called syntactic closures) Strictly speaking the hygiene algorithm is still conservative. Variable bindings declared outside of a macro that are never referenced by a macro don't really need to be renamed. However, modifying the hygiene algorithm to only rename exactly what needs to be renamed seems pretty difficult (especially to do so efficiently). If anyone knows techniques for this definitely let me know (or even better submit a pull request)."
}