Search code examples
macroscrystal-lang

How to define the methods inside module based on passed array of strings?


I'm try to define methods with macro. When I try to build methods (see below) then i see syntax error for expression must be an array, hash or tuple literal, not Var:.

module Test
  def self.get_from_outside(methods)
    build_methods(methods.to_a)
  end

  macro build_methods(methods)
    {% for method in methods %}
      def self.{{method.id}}_present?
        true
      end
    {% end %}
  end
end

t = Test
t.get_from_outside(["method_a", "method_b", "method_c"])

https://carc.in/#/r/45o7

OK, node has type Var and macro doesn't allow pass this type but if I pass array directly the program is successful compile. Now I can't pass parameters outside.

module Test
 METHODS = ["method_a", "method_b", "method_c"]

 {% for method in METHODS %}
   def self.{{method.id}}
     true
   end
 {% end %}
end

t = Test
p t.responds_to?(:method_a)
#=> true

https://carc.in/#/r/45o8

Is it possible to define methods inside the module with outside array?


Solution

  • When working with macros, the probably most important concept to get right is compile time execution versus runtime execution.

    Macros are executed at compile time, as an interpreted language by the compiler. All other other code is executed when the generated binary is run.

    Therefore macros cannot access any runtime data, such as the parameters of a regular method definition. You can go from a macro to the runtime by generating code that puts the data where the runtime expects it, for example a particular variable. But obviously you cannot go the other way.

    So as a rule of thumb, you can pass data from a macro to a method, but not the other way around.