Since it came up in the other thread, TR macros are not used (much) in the standard library because they currently slow down compilations too much. They are also hard to write, not because you end up with wrong optimizations (though that's a possibility too), but because the optimizations are not applied reliably because Nim's AST has too many invisible parts (nkHiddenDeref, implicit type conversions).
Better API design can often (but not always) produce better solutions: table.mgetOrPut, sugar.dup, writing a macro that takes varargs come to mind.
the TRW general idea is useful though:
instead how about introducing something that fits better with the language and is more flexible:
# instead of: template optMul{`*`(a, 2)}(a: int): int = a+a
proc myCustomTRW(a: NimNode): NimNode {.rewrite.} =
## if returns nil, the expression is not rewritten, else it's rewritten
if a.kind == nnkInfix and a[0].strVal == "*":
result = genAst(lhs = a[1], rhs = a[2]): lhs + rhs
the semantics: each expression is matched (recursively) against each TRW in scope
the only question IMO is whether this would be more expensive (at CT) than current TRW, but I don't see why.
the only question IMO is whether this would be more expensive (at CT) than current TRW, but I don't see why.
It looks equally expensive to me, that's not an improvement. ;-)
In my opinion the optimization needs an explicit anchor point. Like so:
proc `*`(x, y: Matrix): Matrix {.anchor: matrixMul.} = ...
proc `+`(x, y: Matrix): Matrix {.anchor: matrixAdd.} = ...
macro matrixAdd(n: NimNode): NimNode =
# turn (a * b) + c into `*+`(a, b, c)
if n[0].matches(matrixMul):
result = newCall("*+", a, b, c)
else:
result = nil # no match
macro optNot*{not a}(a: bool{nkCall}): untyped =
if a.matches(Call[
Sym(strVal: "[]"),
Sym(getTypeInst: Sym(strVal: "Lex")),
.._
]):
warning("Deprecated use of `not lex[]`, convert to `lex.not []`", a)
result = nnkPragmaBlock.newTree(
nnkPRagma.newTree(ident "noRewrite"),
newCall("not", a)
)
The only issue is that {.noRewrite.} pragma seems to be broken again (I'm not 100% sure if I'm using it correctly here, but considering example from the manual also gives repeated macro expansions ...).
template pwnEcho{echo(x)}(x: typed) =
{.noRewrite.}: echo("pwned!")
echo "ab"
Creates
/usercode/in.nim(4, 6) Hint: pwnEcho(["ab"]) --> ' {.noRewrite.}: echo(["pwned!"])' [Pattern]
/usercode/in.nim(2, 22) Hint: pwnEcho(["pwned!"]) --> ' {.noRewrite.}: echo(["pwned!"])' [Pattern]
/usercode/in.nim(2, 22) Hint: pwnEcho(["pwned!"]) --> ' {.noRewrite.}: echo(["pwned!"])' [Pattern]
/usercode/in.nim(2, 22) Hint: pwnEcho(["pwned!"]) --> ' {.noRewrite.}: echo(["pwned!"])' [Pattern]
/usercode/in.nim(2, 22) Hint: pwnEcho(["pwned!"]) --> ' {.noRewrite.}: echo(["pwned!"])' [Pattern]
/usercode/in.nim(2, 22) Hint: pwnEcho(["pwned!"]) --> ' {.noRewrite.}: echo(["pwned!"])' [Pattern]
/usercode/in.nim(2, 22) Hint: pwnEcho(["pwned!"]) --> ' {.noRewrite.}: echo(["pwned!"])' [Pattern]
/usercode/in.nim(2, 22) Hint: pwnEcho(["pwned!"]) --> ' {.noRewrite.}: echo(["pwned!"])' [Pattern]
... N lines omitted
the examples in the docs does no convenience me, why not use a template?
instead of
template optMul{`*`(a, 2)}(a: int): int = a+a
just write:
template `*`(a: int, b: static[2]): int = a+a