Macro methods

Macro defs allow you to define a method for a class hierarchy which is then instantiated for each concrete subtype.

A def is implicitly considered a macro def if it contains a macro expression which refers to @type. For example:

  1. class Object
  2. def instance_vars_names
  3. {{ @type.instance_vars.map &.name.stringify }}
  4. end
  5. end
  6. class Person
  7. def initialize(@name : String, @age : Int32)
  8. end
  9. end
  10. person = Person.new "John", 30
  11. person.instance_vars_names # => ["name", "age"]

In macro definitions, arguments are passed as their AST nodes, giving you access to them in macro expansions ({{some_macro_argument}}). However that is not true for macro defs. Here the parameter list is that of the method generated by the macro def. You cannot access the call arguments during compile-time.

  1. class Object
  2. def has_instance_var?(name) : Bool
  3. # We cannot access name inside the macro expansion here,
  4. # instead we need to use the macro language to construct an array
  5. # and do the inclusion check at runtime.
  6. {{ @type.instance_vars.map &.name.stringify }}.includes? name
  7. end
  8. end
  9. person = Person.new "John", 30
  10. person.has_instance_var?("name") # => true
  11. person.has_instance_var?("birthday") # => false