-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Type Range misidentified as Enumerable #4684
Comments
A Range(Int32, Int32) | Enumerable(Int32) # => Enumerable(Int32) https://play.crystal-lang.org/#/r/2bft That's why |
|
Are you saying that there is no runtime way to detect that a Range is a Range? (When it is union typed with Enumerable) |
No, this is exactly the purpose of
https://crystal-lang.org/docs/syntax_and_semantics/is_a.html |
The branch happens at runtime, but the definition happens at compile time, if I remember correctly. Maybe I'm wrong, or a bit confused, thought. |
Reduced: def test(v)
if v.is_a? Range
puts "Range!"
else
pp v, typeof(v), v.class
end
end
puts "works for ordinary var"
test(rand < 1.5 ? (1..3) : [1,2,3])
puts "don't work for Enumerable"
test((rand < 1.5 ? (1..3) : [1,2,3]).as(Enumerable(Int32))) https://carc.in/#/r/2bjq
Looks like a bug IMHO. |
My workaround for the moment will be to do the comparison using v.class== Range(Int32,Int32). |
It appears I can't use #class because it does not seem to restrict/guarantee type like === or value.is_a? does. |
Even more reduced: module A(T)
end
class B(T)
include A(T)
end
class C(T)
include A(T)
end
def test(v)
if v.is_a? B
puts "B!"
else
pp v, typeof(v), v.class
end
end
puts "works for ordinary var"
test(rand < 1.5 ? B(Int32).new : C(Int32).new)
puts "don't work for Enumerable"
test((rand < 1.5 ? B(Int32).new : C(Int32).new).as(A(Int32))) https://carc.in/#/r/2bmz About workaround - i don't know. |
OP's example for current Crystal versions: require "yaml"
module X
class Y
alias Virtual = Range(Int32, Int32) | Enumerable(Int32)
include YAML::Serializable
property year : Virtual?
def initialize
end
end
end
v = X::Y.new
v.year = 2015..2017
value = v.year
p! value.inspect # => "2015..2017"
p! value.class # => Range(Int32, Int32)
p! value.is_a?(Range) # => false
p! value.is_a?(Range(Int32, Int32)) # => false
p! Range === value # => false
p! typeof(value) # => (Enumerable(Int32) | Nil) |
Hello,
Here is an example that demonstrates how, when Enumerable is present in the aliased type, a Range object no longer reports true for is_a?( Range).
When the above code is run, the "puts" prints the following:
The same code on play: https://play.crystal-lang.org/#/r/2bfo
Thanks
The text was updated successfully, but these errors were encountered: