-
-
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
Wonky type matching with recursive type aliases #6521
Comments
This is expected behaviour. Every other core team member reading this: can we pretty please remove recursive types already? :-) |
Shouldn't the function change x's type from Array(Int32) to Array(Magic), though? |
Sorry, I might have misunderstood the issue. Could you please describe what's the problem? It's not clear just by seeing a code snippet with no explanation about what you expect and what you thing is not behaving as expected. |
Actually, I don't know if this has anything to do with recursive aliases (they should probably removed nonetheless). It behaves the same if alias Magic = Int32 | Array(Int32)
def magic(x)
case x
when Int32 then puts "a"
when Array(Int32) then puts "b"
else puts "c"
end
end
magic 2_i32 # output: a
magic [2_i32, 3_i32] # output: b
puts
magic 2_i32.as Magic # output: a
magic [2_i32.as(Magic), 3_i32.as(Magic)] # output: c |
No. |
What I expected was to only have to include the types that are explicitly defined as part of the type alias in the case branches. |
Maybe this is a better example: alias Magic = Int32 | Array(Magic)
def magic(x : Magic)
case x
when Array(Magic) then puts "a"
when Int32 then puts "b"
else puts "c"
end
end
magic [2_i32, 3_i32] # output: c |
It's possible to actually go around the issue by matching on "Array" alone btw. |
Please include in your example "I expected this to happen but this happens instead". Just seeing "output: c" doesn't tell us anything. For us, that's expected behavior. |
But basically, |
So, for this example: alias Magic = Int32 | Array(Magic)
def magic(x : Magic)
case x
when Array(Magic) then puts "a"
when Int32 then puts "b"
else puts "c"
end
end
magic [2_i32, 3_i32] Because Magic only explicitly includes Int32 and Array(Magic) in it's definition, I expect the two case branches to match on every possible input. |
Yeah, I think it's a bit confusing that I think the method shouldn't be matched. But then again, type restrictions sometimes work in a different way then checks performed by |
For example: class Foo
end
class Bar < Foo
end
def magic(x : Array(Foo))
case x
when Array(Foo) then puts "is an array of Foo"
else puts "not an array of Foo"
end
end
magic [Bar.new] # This is Array(Bar) The above compiles, because The reason it gives class Foo
end
class Bar < Foo
end
def magic(x : Array(Foo))
if x.is_a?(Array(Foo))
x << Foo.new
end
end
magic [Bar.new] Imagine if So maybe the type restriction shouldn't match? Well, the type restriction assumes we are not going to put anything inside the array, or generic type. But # We want to match ranges of any integer types
def foo(x : Range(Int, Int))`
end If we change that rule, we won't be able to generically define methods for ranges of ints, or for array of ints, etc. That said, maybe that change is a good thing, and type restrictions should behave exactly as an |
We need something like: # Assume we are only going to read from the Array, so checks for subclasses of Foo work
def foo(x : Array(out Foo))
end
# Assume we are going to put things inside an Array, so Array(Bar) where Bar < Foo won't match
def foo(x : Array(in Foo))
end This is present in C#, for example. However, it maybe makes the language too complex in my opinion. But all of this has to do with covariance and contravariance, which was never really solved/implemented well in Crystal. |
Okay, now the issue is visible. And again, this isn't specific to recursive aliases (maybe it's more obvious though). What you want can be expressed with method overloads instead of alias Magic = Int32 | Array(Magic)
def magic(x : Array(Magic))
puts "a"
end
def magic(x : Int32)
puts "b"
end
magic [2_i32, 3_i32] # => a I have already brought the confusingly different behaviour of argument restrictions and |
Related to #3803 |
Closing because this bug or weird behavior is already described in #3803 |
The text was updated successfully, but these errors were encountered: