3

In Crystal, is it possible to view metadata on a type's method at compile time? For example, to determine the number of arguments the method accepts, what the type restrictions on the arguments are, etc.

Looking through the API, the compiler's Def and Arg macros have methods which supposedly return this meta information, but I can't see a way of accessing them. I suspect the meta information is only accessible by the compiler.

Johannes Müller
  • 5,581
  • 1
  • 11
  • 25
John
  • 9,249
  • 5
  • 44
  • 76

2 Answers2

3

I found out how to do it. I was looking in the wrong place of the API. The Crystal::Macros::TypeNode has a methods macro which returns an array of method Def (which is how you can access them). It looks like the TypeNode class is the entry point to a lot of good macros.

Usage example

class Person
  def name(a)
    "John"
  end

  def test
    {{@type.methods.map(&.name).join(', ')}}
  end
end

Or

{{@type.methods.first.args.first.name}}

Simply returning the argument name posses an interesting problem, because, after the macro interpreter pastes it into the program, the compiler interprets the name as a variable (which makes sense).

But the real value happens in being able to see the type restrictions of the method arguments

class Public < Person
  def name(a : String)
    a
  end

  def max
    {{@type.methods.first.args.first.restriction}}
  end
end

Person.new.max # => String
John
  • 9,249
  • 5
  • 44
  • 76
  • 1
    Great, if this fits what you were looking for. But note that this is *not* runtime reflection. Macros are expanded at compile time so all the things you can get through your macros from `TypeNode` are fixed at that point. – Johannes Müller Sep 05 '18 at 22:28
  • @JohannesMüller my question doesn't ask about *runtime reflection*. I simply asked if it was possible to view method names at runtime. And it is. – John Sep 05 '18 at 23:21
  • 1
    This being said, I do appreciate the response. – John Sep 05 '18 at 23:21
  • 1
    Pardon me, but the very first sentence asks to "view metadata on an object's method at runtime". That explicitly describes runtime reflection. Your answer doesn't provide that because you can't just ask an object about I its method names. – Johannes Müller Sep 06 '18 at 05:13
  • In Java for example, you can so that with `var.class.getDeclaredMethods()`. – Johannes Müller Sep 06 '18 at 05:20
  • 1
    @JohannesMüller Knowing what my goal was, how would you phrase the question? – John Sep 06 '18 at 10:59
  • 1
    Maybe don't ask for runtime information on an object but compile time information on a type? – Johannes Müller Sep 06 '18 at 13:52
0

I suspect the meta information is only accessible by the compiler.

Exactly. Crystal does not have runtime reflection. You can do a lot with macros at compile time, but once it is compiled, type and method information is no longer available.

But, since everything in a program is known a compile time, you shouldn't really need runtime reflection.

Johannes Müller
  • 5,581
  • 1
  • 11
  • 25
  • 1
    This is incorrect. Turns out you can access the methods on a class by going through the `TypeNode`. Also, I take issue with ~"because the program is compiled, you shouldn't need runtime reflection." Technically, I don't even *need* the ability to multiply numbers in a language. I could just stick with addition (adding things again and again and again). Or in the case of this problem, I could use another language to analyze my crystal source, get the list of methods, and copy paste them into the crystal program. But thankfully, the crystal folks let me access the methods using the `TypeNode`. – John Sep 05 '18 at 19:57
  • 1
    My statement is indeed valid. But it seems you asked the wrong question (runtime reflection) when you actually just care about compile time reflection. – Johannes Müller Sep 05 '18 at 22:30