crystal-lang / crystal

The Crystal Programming Language
https://crystal-lang.org
Apache License 2.0
19.34k stars 1.61k forks source link

#subclasses method #3768

Closed jasonl99 closed 7 years ago

jasonl99 commented 7 years ago

I'm trying to write a method that returns an array of subclasses (the actual class, not the string). I have it working, but I think this is a bit of a hack.

The class variable @@subclasses gets typed properly when I instantiate it with the Animal class, and works as you can see by the output. But I also have to shift the array to remove the abstract class. It seems I should be able to do something like @@subclasses = [] of Animal though this would just be an array of instances, not classes. Is there a special grammar for types of this sort?


abstract class Animal
  @@subclasses = [Animal]  # specified manually!
  @@subclasses.shift       # drop the class leaving the array properly typed and empty

  def self.subclasses
    @@subclasses
  end

  macro inherited
    Animal.subclasses << {{@type.name}}
  end

end

class Dog < Animal
end

class Cat < Animal
end

class Terrier < Dog
end

Animal.subclasses               # [Dog, Cat, Terrier] : Array(Animal)
Animal.subclasses[0].new    # <Dog:0x1387f40>   Animal
Sija commented 7 years ago

Yeah, there's Crystal::Macros::TypeNode#subclasses.

abstract class Animal; end

class Dog < Animal; end
class Cat < Animal; end

class Terrier < Dog; end

# print all subclasses
pp {{ Animal.all_subclasses }} # => [Dog, Terrier, Cat]
# print only direct subclasses
pp {{ Animal.subclasses }} # => [Dog, Cat]
jasonl99 commented 7 years ago

Thank you!