Using Crystal Macros to Generate Getter Methods
Crystal macros let you generate getter methods at compile time, keeping code DRY while preserving static type safety.
08 Apr 2026, 03:45 UTC

Useful answer
Crystal macros let you generate repetitive boilerplate code during compilation, keeping source files DRY while preserving static type safety.
How it works
A macro receives compile‑time AST nodes, expands them into Crystal source, and the compiler treats the expanded code as if you had written it yourself.
Example macro
# Define a macro that creates a getter for each symbol passed
macro getter(*names)
{{ names.each |name| }}
def {{name}}; @{{name}} end
{{ end }}
end
# Use the macro inside a class
class Point
getter :x, :y
end
When the compiler encounters getter :x, :y, it expands the macro into:
def x; @x end
def y; @y end
Verification
To confirm the expansion, run:
crystal tool --preprocess example.cr
The pre‑processed output will show the generated method definitions. Then compile and execute a small test:
crystal build example.cr && ./example
Inside example.cr you can instantiate Point and call p.x and p.y to retrieve the instance variables.
Limits
- Macros execute only at compile time; they cannot inspect runtime values or perform I/O.
- Any error that occurs during macro expansion is reported at the macro call site, which can make debugging harder.
- Because the generated code is inserted before type checking, a macro that produces ill‑typed code will cause a compile‑time error, but the error message points to the macro call rather than the generated line.
Common mistakes
- Using a macro for logic that could be a regular method – macros add compile‑time overhead and reduce readability.
- Forgetting the double‑curly syntax
{{ }}(or{% %}for control flow) leads to syntax errors that appear as unexpected tokens. - Assuming macro arguments are evaluated at runtime; they are actually AST nodes, so you cannot treat them as ordinary values inside the macro body without using
{{ }}interpolation. - Over‑generating code: large macros that produce many methods can inflate compile times and obscure the flow of control.
By keeping macros focused on simple, repetitive definitions like getters, you retain Crystal’s static guarantees while reducing boilerplate.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.