Macro Recipes¶
Use this page for small MakrellPy macro patterns that you can adapt directly.
For the broader model behind meta and macros, continue with
Metaprogramming.
Start with the checked-in showcase¶
MakrellPy already has a compact public macro example in
impl/py/examples/macros/showcase.mr.
Run it:
makrell showcase.mr
That example is worth reading because it shows three different kinds of macro:
piperewrites a sequence of forms into pipeline stylerpnreads postfix input and builds ordinary Makrell syntaxlisphosts a Lisp-like round-bracket notation inside Makrell
If you only read one macro example first, make it that one.
Recipe: duplicate an expression¶
{def macro twice [x]
[{quote $x} {quote $x}]}
{twice {print "hello"}}
This is the smallest useful macro shape:
receive syntax
return syntax
keep the transformation obvious
It is a good first macro because you can inspect what it receives and what it returns without much surrounding machinery.
Recipe: reshape a whole form with regular¶
{def macro second [ns]
ns = {regular ns}
{quote {unquote ns@1}}}
{second 2 3 5}
Use regular when you want the macro arguments as a more ordinary list of
nodes rather than relying on raw spacing or other structure details.
This is the pattern behind many practical macros:
normalise the incoming nodes
pick out the parts you need
build a new expression with
quoteandunquote
Recipe: keep helper logic in meta¶
{meta
{fun wrap_print [label expr]
{quote
{print {unquote label}}
{unquote expr}}}}
{def macro announce [ns]
ns = {regular ns}
{wrap_print ns@0 ns@1}}
Keeping helper logic in meta is usually easier to read than packing all the
logic into one large def macro body.
This split is often a good default:
metafor compile-time helper functions and valuesdef macrofor the syntax transformation entry point
Recipe: wrap a block with setup and teardown¶
{def macro timeit [ns]
[
{quote {import time}}
{quote start = {time.time}}
] + ns + [
{quote {print "Time taken:" {time.time} - start}}
]}
This kind of macro is useful when you want to inject repeated structure around a user-provided block.
It is a good pattern for:
instrumentation
tracing
repeated setup
repeated cleanup
Recipe: build a tiny language, not just a shortcut¶
The checked-in pipe, rpn, and lisp examples are useful because they
show three different macro ambitions:
ergonomic rewriting
alternative notation
embedded language surface
That is usually where Makrell macros become most interesting: not only saving a few characters, but making a different structural style possible.
Practical advice¶
When a macro becomes hard to understand:
shrink it to the smallest failing example
inspect the incoming nodes first
move helper logic into
metakeep generated structure explicit with
quote
Also remember that macros can introduce bindings. Generated names may still collide with user code, so readability and restraint matter.