Hello, I have a small feature proposal that I didn't feel comfortable making an
RFC for.
**Synopsis**
Introduce a "wrap" statement which is allowed only on top-level code. `wrap X`
replaces the AST of the entire program on all following lines with the result
of passing it through the macro `X`.
**Example**
# a.nim
macro implementRecur(n: untyped): untyped =
## Allows functions to refer to themself by calling ``recur()``
## To suppress this behavior, annotate the function with
``{.literalRecur.}``
...
Run
# b.nim
import a
wrapwith a.implementRecur
proc factorial(n: int): int =
if n == 0:
return 1
return n * recur(n - 1)
proc funky(): bool {.literalRecur.} =
let recur: bool = false
return recur
Run
**Rationale**
1\. Redundancy
Currently, the most typical way to implement a macro similar to
`implementRecur` would be to implement a `recursive` macro which operates on
function definitions. Then, all function definitions that want to reap the
benefits of this behavior must be annotated with `{.recursive.}`.
To me, this feels redundant. I don't want to have to say "hey, I'm using the
`recur` keyword over here"" every time I use the `recur` keyword; it should be
obvious by the fact that I'm _using the ``recur`` keyword_.
_Post-script_ : But, okay, what if I actually meant to refer to a variable
named `recur`? That's what `{.literalRecur.}` is for. You have to annotate one
case or the other; it would be nice to annotate the (likely) rarer one (in this
case, `literalRecur`). If referring to `recur` literally is more common in a
use-case, don't use wrapwith and instead annotate each function.
2\. Bridging the gap between macro classes
Consider the `=>` macro from `sugar`. It requires no special annotation or
extra code in order to use; the macro itself is enough. Why? Because Nim
happens to provide sufficient tools for it to work (namely, `auto`). Should
these tools not have existed, `=>` would need an annotation much like
`implementRecur` or `{.recursive.}`.
I posit that the reason the example recur macro _does_ need annotation is
simply because Nim happens to not supply enough tools for it to do its job. If,
for instance, macros had the power to traverse up the AST as well as down,
`implementRecursive` and `{.recursive.}` wouldn't be needed; a `macro
recur(ast: untyped): untyped)` would be sufficient.
Noting this, I claim that these two macros are of "equal conceptual power".
That is, they work on a similar enough level that I claim that _neither_ should
require annotation.
Now, the example recur macro _does_ need annotation, the best we can do is to
make it as unobtrusive as possible. This is what `wrapwith` is about.
3\. Equal power to existing constructs
This proposal does not add any novel behavior to the language. `wrapwith X \n
restOfCode` may be currently written instead as `X: \n\t restOfCode`. Since no
novel behaviour is added, the proposal can (in some sense) be considered fairly
safe.
4\. Git diff
Removing or adding a DSL will no longer require a change in indentation, which
is much nicer for VCS.
**PostScript**
I've had some trouble reifying my thoughts here, largely because a lot of the
reason that I think this should be a feature is simply because it "feels right"
to me. Arguments 1 & 2 are somewhat philosophical and perhaps poorly explained.
If so, I apologize, and they may be ignored.