|
Maybe this seems like a bit strange design. Couldn't `<!` itself be made a macro, so that it doesn't matter where it is, instead of `<!` being something to transform? EDIT: No, it couldn't, because then that `<!` macro wouldn't know enough context, as it typically cannot look into outer scopes. Another thing that comes to my mind reading this is, that in Guile I wrote a macro for function contracts. This macro references a unique value as the thing that should be replaced. That unique value can be imported from another module and aliased, so that name clashes are avoided. In the case of function contracts, I have a special define form `define-with-contract`, which recognizes the placeholder, where it should insert the result of applying function, to check the output contract of the function. I guess, if the placeholder made sense in other places than the contract definition/specification, then I would face the same problem, of the macro not seeing it in helper functions. Say for example I use the unique placeholder value inside a helper function, which I then use inside the contract definition. I guess in case of a placeholder value, it couldn't be a macro itself, because then it would lack context about what should be in its place, as the scope would then be too limited. The basic problem seems to be as you already explained, that macros cannot look inside function bodies hidden behind calls. Sort of like macros can only deal with "one layer", which is the syntax that is passed to the macro, but if that syntax contains calls to functions, then the macro is clueless about what happens inside those functions. I wonder if this limitation is present for all lisps, or if there is some macro system or method of designing them, that circumvents this problem. But also it seems, that this is only (?) a limitation, when the macro needs to look for something specific like a placeholder. For a timing macro for example, there is no such thing and expressions merely get reordered and maybe wrapped in some `let` or something. |
In clojure that might look like this:
Which would, hypothetically, convert to something like: If `<!` was the macro, it wouldn't be able to see the (after a) only the `(<! ch)`. `go` is the only part that can see both the code before the await and the code after the await, and it splits it at the await so it can suspend execution until the take has resolved.EDIT: You edited while I was typing and came to the same realisation!
> I guess in case of a placeholder value, it couldn't be a macro itself, because then it would lack context about what should be in its place, as the scope would then be too limited.
In the example you gave, it could be a macro that transformed into a function call: eg `(foo my-placeholder)` turns into `(foo (read-placeholder :my-placeholder))` and that function read the registered value from a placeholder registry. It wouldn't literally transform the placeholder into the set value at compile time, but it could dynamically inject the value at runtime.
> I wonder if this limitation is present for all lisps, or if there is some macro system or method of designing them, that circumvents this problem.
I would imagine so.
Its theoretically possible that you could see the full AST + environment so that if you see a function call, you can look up the function in the environment and then access its AST. I've never heard of any language (lisp or otherwise) that did this. It could exist, though.