Reputation: 784
I was using Enumerable#select(&block) to try to refine an Array to elements of a certain type with arr.select { |el| el.is_a?(Bar) }
but that wasn't working.
I then saw the Enumerable#select(type) method, and that worked: https://play.crystal-lang.org/#/r/7v05
But I noticed that the two definitions are quite similar in my case.
def select(&block : T ->)
ary = [] of T
each { |e| ary << e if yield e }
ary
end
def select(type : U.class) forall U
ary = [] of U
each { |e| ary << e if e.is_a?(U) }
ary
end
Is there a way to let the compiler know that the select block is refining the type of the elements (maybe by adding a type to the block somehow)? Or is there a reason the compiler can't know about what the block is asserting?
Upvotes: 0
Views: 78
Reputation: 4857
The issue here is that the array needs to be created with the right type. So the core difference of the two methods is:
ary = [] of T
where T
is the type argument of the Enumerable
you invoke select on, versus
ary = [] of U
where U
is the type argument specific to this method (forall U
).
So to do what you want, we would need to know that the block is filtering the elements, but that's in no way encoded in the blocks type. It only has a list of argument types and a return type. However, we could certainly combine the two methods into something like:
module Enumerable(T)
def select(type : U.class) forall U
ary = [] of U
each { |e| ary << e if yield e if e.is_a? U }
ary
end
end
Upvotes: 2