Crystal: Macro @type.superclasses

Created on 11 Jan 2017  路  9Comments  路  Source: crystal-lang/crystal

It's currently possible to create an array of subclasses in a macro:

class Hierarchy
  def self.subclasses
    {{@type.all_subclasses}}  # using all_subclasses for illustration
  end
end

class A < Hierarchy; end
class B < A; end
class C < B; end

puts A.subclasses  # [B,C]

It would be nice to also have a similar method for superclasses:

class Hierarchy
  def self.superclasses
    {{@type.superclasses}}  #superclasses doesn't currently exist
  end
end

puts C.superclasses # [B, A, Hierarchy], as proposed

Is there any way to do this currently? I can't seem to figure out a way to leverage @type.superclass to walk up the class hierarchy.

in-progress feature accepted compiler

Most helpful comment

Looks nice! Could you send a PR with this, including some specs (covering generic classes as well as plain classes) and docs? We'll be happy to merge it.

One thing to discuss: though I like the name ancestors, it has a slightly different semantic in Ruby, where it also returns the included modules. This could cause some confusion for people coming from the Ruby world, so perhaps superclasses or base_classes could be a better name.

All 9 comments

Like this? (needs some patch)

class Foo
  def self.ancestors
    {{@type.ancestors}}
  end
end

class A < Foo; end
class B < A; end

puts B.ancestors # [A, Foo, Reference, Class]

I don't know if this is necessary for Crystal, if yes, the git-formatted-patch below can be just simply applied:

From a44a6319238df017fe88df32e9e805a2109f8f7f Mon Sep 17 00:00:00 2001
From: David Kuo <[email protected]>
Date: Wed, 11 Jan 2017 17:18:26 +0800
Subject: [PATCH] [Compiler] Available getting ancestors in macro

---
 src/compiler/crystal/macros/methods.cr | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/compiler/crystal/macros/methods.cr b/src/compiler/crystal/macros/methods.cr
index 7ce4646..fb459ed 100644
--- a/src/compiler/crystal/macros/methods.cr
+++ b/src/compiler/crystal/macros/methods.cr
@@ -1295,6 +1295,8 @@ module Crystal
       case method
       when "abstract?"
         interpret_argless_method(method, args) { BoolLiteral.new(type.abstract?) }
+      when "ancestors"
+        interpret_argless_method(method, args) { TypeNode.ancestors(type) }
       when "union?"
         interpret_argless_method(method, args) { BoolLiteral.new(type.is_a?(UnionType)) }
       when "union_types"
@@ -1464,6 +1466,10 @@ module Crystal
       NilLiteral.new
     end

+    def self.ancestors(type)
+      ArrayLiteral.map(type.ancestors) { |ancestor| TypeNode.new(ancestor) }
+    end
+
     def self.subclasses(type)
       ArrayLiteral.map(type.subclasses) { |subtype| TypeNode.new(subtype) }
     end
--
2.7.4

Yes, that's perfect!

Looks nice! Could you send a PR with this, including some specs (covering generic classes as well as plain classes) and docs? We'll be happy to merge it.

One thing to discuss: though I like the name ancestors, it has a slightly different semantic in Ruby, where it also returns the included modules. This could cause some confusion for people coming from the Ruby world, so perhaps superclasses or base_classes could be a better name.

I agree withe the name of #superclasses since there's also a #subclasses @type method.

@spalladino Type#ancestors acts as Ruby's (both include modules), only the different is Ruby includes itself (because of the Module#prepend)

# Crystal:
module FooBar; end
class Foo; end
class Bar < Foo
  include FooBar
end

puts Bar.ancestors # [FooBar, Foo, Reference, Class]

# Ruby
module FooBar; end
class Foo; end
class Bar < Foo
  include FooBar
end
puts Bar.ancestors # [Bar, FooBar, Foo, Object, Kernel, BasicObject]

module FooBar2; end
Bar.send :prepend, FooBar2
puts Bar.ancestors # [FooBar2, Bar, FooBar, Foo, Object, Kernel, BasicObject]

I can send a PR with ancestors and the version only shows the inherit chain (maybe named superclasses)

Let's go for ancestors then. This is what the compiler uses internally and the Ruby difference is minor. I prefer the Crystal behavior BTW.

My bad. I agree with @ysbaddaden then, ancestors it is.

It seems this was implemented in #3875

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sergey-kucher picture sergey-kucher  路  66Comments

asterite picture asterite  路  70Comments

RX14 picture RX14  路  62Comments

malte-v picture malte-v  路  77Comments

asterite picture asterite  路  60Comments