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.
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"
# ...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With