async function foo() {
before()
const bar = await take(ch)
after(bar)
}
its basically sugar for:
function foo() {
before()
take(ch, function (bar) {
after(bar)
})
}
Where the callback passed to take will be called when take resolves. And other async functions can run in the meantime.
In clojure that might look like this:
(go
(before)
(let [a (<! ch)]
(after a)))
Which would, hypothetically, convert to something like:
(do
(before)
(<! ch (fn [a]
(after a))))
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.
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.