Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting info about inheritance chain in Crystal

Just out of curiosity and to learn a little bit about the general structure of Crystal, I was looking for some reflection features that would allow me to better understand how the inheritance chain is built.

I was thinking something like ruby's superclass, ancestors or included_modules methods.

Is there something like that available in Crystal language?

Moreover, it would be useful to have some kind of diagram that could show me the big picture.

like image 271
opensas Avatar asked Jan 29 '23 17:01

opensas


1 Answers

Metaprogramming in Crystal is achieved using macros.

Macros are methods that receive AST nodes at compile-time and produce code that is pasted into a program.

Crystal already has implementations of superclass and ancestors which return results at compile time. So you can do:

{{ pp MyClass.superclass }}
{{ pp MyClass.ancestors }}

For convenience, to inspect the inheritance you can write your own macros. Having learning purpose in mind, it may look like this:

class Class
  def superclass
    {{ @type.superclass }}
  end

  def ancestors
    {% if @type.ancestors.size > 0 %}
      {{ @type.ancestors }}
    {% else %}
      [] of Nil
    {% end %}
  end

  def included_modules
    {% if @type.ancestors.any? { |a| a.class.stringify.ends_with?(":Module") } %}
      {{ @type.ancestors.select { |a| a.class.stringify.ends_with?(":Module") } }}
    {% else %}
      [] of Nil
    {% end %}
  end

  def inheritance_chain
    String.build do |chain|
      cls = self
      chain << cls
      while !(cls = cls.superclass).nil?
        chain << " > #{cls}"
      end
    end
  end
end

Then you can do the inspection:

class A
end

module B
end

require "crystal/system/random"

class C < A
  include B
  include Crystal::System::Random
end

C.name             # => "C"
C.superclass       # => A
C.ancestors        # => [Crystal::System::Random, B, A, Reference, Object]
C.included_modules # => [Crystal::System::Random, B]
A.included_modules # => []

And if you go further:

C.superclass                                                    # => A
C.superclass.try &.superclass                                   # => Reference
C.superclass.try &.superclass.try &.superclass                  # => Object
C.superclass.try &.superclass.try &.superclass.try &.superclass # => nil

And now using inheritance_chain:

Int32.inheritance_chain                          # => "Int32 > Int > Number > Value > Object"
String.inheritance_chain                         # => "String > Reference > Object"
Float64.inheritance_chain                        # => "Float64 > Float > Number > Value > Object"
Array(Bool).inheritance_chain                    # => "Array(Bool) > Reference > Object"
Hash(Bool, Bool).inheritance_chain               # => "Hash(Bool, Bool) > Reference > Object"
Tuple(Char).inheritance_chain                    # => "Tuple(Char) > Struct > Value > Object"
NamedTuple(s: String, b: Bool).inheritance_chain # => "NamedTuple(s: String, b: Bool) > Struct > Value > Object"
Nil.inheritance_chain                            # => "Nil > Value > Object"
Regex.inheritance_chain                          # => "Regex > Reference > Object"
Symbol.inheritance_chain                         # => "Symbol > Value > Object"
Proc(Int32).inheritance_chain                    # => "Proc(Int32) > Struct > Value > Object"
Set(String).inheritance_chain                    # => "Set(String) > Struct > Value > Object"
Exception.inheritance_chain                      # => "Exception > Reference > Object"
Class.inheritance_chain                          # => "Class > Value > Object"

# union
alias UnionType = Int32 | Nil | String
UnionType.inheritance_chain                      # => "(Int32 | String | Nil) > Value > Object"

# nilable
Int32?.inheritance_chain                         # => "(Int32 | Nil) > Value > Object"

# pointer
alias Int32Ptr = Int32*
Int32Ptr.inheritance_chain                       # => "Pointer(Int32) > Struct > Value > Object"

# ...
like image 124
Vitalii Elenhaupt Avatar answered Feb 07 '23 14:02

Vitalii Elenhaupt