Array co-something inconsistency

Hello,

This code (https://carc.in/#/r/5whi):

class Foo
end

class Bar < Foo
end

ary = Array(Foo.class).new
ary << Bar

Compiles nicely, while when replacing class with module (https://carc.in/#/r/5whj):

module Foo
end

class Bar
  include Foo
end

ary = Array(Foo.class).new
ary << Bar

It failes to compile with no overload matches 'Array(Foo:Module)#<<' with type Bar.class.

Is that intended? Is that going to change?

Thanks,

2 Likes

I suspect what you’ve intended to write is:
https://play.crystal-lang.org/#/r/5wp0

module Foo
end

class Bar
  include Foo
end

ary = Array(Foo).new
ary << Bar.new()
p! ary

No, I’m absolutely sure that I need to store classes, not their instances.

Probably not intended nor designed. I don’t recall any story about the metaclass design related to modules and classes that are including them.

What is what you are trying to achieve later with that array?

1 Like

There are two things here. First, it’s OK for the code not to compile because when you include a module only the instance type gets included, so technically the Bar class is not a Foo class. You can see this is like that because you can’t invoke class methods of Foo from Bar.

module Foo
  def self.foo
    1
  end
end

class Bar
  include Foo
end

Bar.foo # undefined method 'foo' for Bar.class

However, I think it should work when extending the module… but it doesn’t.

Also, as @bcardff says, all of this hasn’t been well defined yet.

1 Like

That’s interesting. However, even when extending, it doesn’t work as well (https://carc.in/#/r/5wyg):

module Foo
end

class Bar
  extend Foo
end

ary = Array(Foo.class).new
ary << Bar
Error in line 9: no overload matches 'Array(Foo:Module)#<<' with type Bar.class
Overloads are:
 - Array(T)#<<(value : T)

Nevertheless, my use-case is Event-Driven Architecture (EDA), where a number of subscribers can subscribe to an Event. For performance benefit, it’s smart to store them in a Hash(Event.class, Array(Proc(Event, Nil)) for faster reaction times. Currently the code looks like this:

struct MyEvent < Event
  getter foo

  def initialize(@foo : String)
  end
end

channel.subscribe(MyEvent) { |event| do_something(event.foo) }

It works nicely when Event is abstract struct, but I’d like to make it a module – just for more beautiful (subjectively) code. :leaves:

Summing it up, Hash(Event.class, Proc(Event, Nil)) doesn’t work when Event is a module, as well as Set(Event.class) and Array(Event.class), as stated in the first post.

P.S: I managed to store Procs with abstract Event in the hash as a pair of Void*.
P.P.S: For better understanding, I have a code sample for channel: https://gist.github.com/vladfaust/455b343f539188af43a91188d06a1a07

Okay, as always, after posting I understand that module would not work for me in my case because all_subclasses for a module doesn’t make sense. And I need to know subclasses for my arch.

However, the issue still remains, and I cannot use a module Foo's includer in the place of Foo.class.

How about Array(Foo.module)?