-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Dispatch pitfall due to Type{Union{}} <: Type{<:MyType}
.
#33780
Comments
Would it be consistent and possible to have an implicit open lower bound in Generally it just doesn't seem very useful to allow Union in this circumstance, in fact I'd be extremely surprised if it were intended by the average user, and it's also related to of other problems such as method invalidation due to spurious ambiguities (mentioned in JuliaLang/www.julialang.org#794) |
It's not possible to do this in 1.x, right? Changing something fundamental like the syntax for talking to the types system sounds like impossible to argue that it is a "minor change" while I do agree that most use-cases of But can we still introduce some open lower bound syntax? Somewhat related?: Explicit concrete type constraint in UnionAll #30363 |
By the way, here is another surprising behavior: julia> Base.promote_rule(Union{}, Int)
Float64
julia> @which Base.promote_rule(Union{}, Int)
promote_rule(::Type{var"#s822"} where var"#s822"<:AbstractIrrational, ::Type{T}) where T<:Real in Base at irrationals.jl:42 Though julia> promote_type(Union{}, Int)
Int64 |
While changing the syntax is impossible, I thought it might be possible to subtly change the of the "no lower bound" case to exclude |
I'd say, strictly speaking, it is a breaking change if it changes the behavior of any code that only depends on defined behaviors. Yes, I know that's too strict and also most of the API is not defined enough anyway. IIUC that's why we have "minor change" as an escape hatch. But I don't think even "minor change" is appropriate for something fundamental like the type system. |
Well I certainly don't have knowledge of the type system theory or implementation to judge what havoc this idea would cause ;-) I'm more wondering whether it's justified from a user perspective. My guess is that when people write |
I think it's cloes to the 0.6 scoping on REPL. No one writes I think it's reasonable to asume that there is code like struct TypeContainer{T}
x::Type{<:T}
end which is called via Adding a new syntax/API sounds like a better solution to me. |
Based on the new morespecific rule for Union{} and method definitions of the specific form `f(..., Type{Union{}}, Vararg)`. If a method definition exists with that specific form, the intersection visitor will ignore all intersections that have that as their only result, saving significant effort when working with lookups involving `Type{<:T}` (which usually ended up mostly ambiguous anyways). Fixes: #33780 This pattern turns out to have still to been making package loading slow. We could keep adding methods following the ambiguity pattern #46000 for the couple specific functions that need it (constructor, eltype, IteratorEltype, IteratorSize, and maybe a couple others) so the internals can detect those and optimize functions that have that method pair. But it seems somewhat odd, convoluted, and non-obvious behavior there. Instead, this breaks all ambiguities in which Union{} is present explicitly in favor of the method with Union{}. This means that when computing method matches, as soon as we see one method definition with Union{}, we can record that the method is the only possible match for that slot. This, in essence, permits creating a rule for dispatch that a TypeVar lower bound must be strictly a supertype of Union{}, but this creates it at the function level, instead of expecting the user to add it to every TypeVar they use to define methods. This also lets us improve the error message for these cases (generally they should error to avoid polluting the inference result), since we can be assured this method will be called, and not result in an ambiguous MethodError instead! Reverts the functional change of #46000
Based on the new morespecific rule for Union{} and method definitions of the specific form `f(..., Type{Union{}}, Vararg)`. If a method definition exists with that specific form, the intersection visitor will ignore all intersections that have that as their only result, saving significant effort when working with lookups involving `Type{<:T}` (which usually ended up mostly ambiguous anyways). Fixes: #33780 This pattern turns out to have still to been making package loading slow. We could keep adding methods following the ambiguity pattern #46000 for the couple specific functions that need it (constructor, eltype, IteratorEltype, IteratorSize, and maybe a couple others) so the internals can detect those and optimize functions that have that method pair. But it seems somewhat odd, convoluted, and non-obvious behavior there. Instead, this breaks all ambiguities in which Union{} is present explicitly in favor of the method with Union{}. This means that when computing method matches, as soon as we see one method definition with Union{}, we can record that the method is the only possible match for that slot. This, in essence, permits creating a rule for dispatch that a TypeVar lower bound must be strictly a supertype of Union{}, but this creates it at the function level, instead of expecting the user to add it to every TypeVar they use to define methods. This also lets us improve the error message for these cases (generally they should error to avoid polluting the inference result), since we can be assured this method will be called, and not result in an ambiguous MethodError instead! Reverts the functional change of #46000
Based on the new morespecific rule for Union{} and method definitions of the specific form `f(..., Type{Union{}}, Vararg)`. If a method definition exists with that specific form, the intersection visitor will ignore all intersections that have that as their only result, saving significant effort when working with lookups involving `Type{<:T}` (which usually ended up mostly ambiguous anyways). Fixes: #33780 This pattern turns out to have still to been making package loading slow. We could keep adding methods following the ambiguity pattern #46000 for the couple specific functions that need it (constructor, eltype, IteratorEltype, IteratorSize, and maybe a couple others) so the internals can detect those and optimize functions that have that method pair. But it seems somewhat odd, convoluted, and non-obvious behavior there. Instead, this breaks all ambiguities in which Union{} is present explicitly in favor of the method with Union{}. This means that when computing method matches, as soon as we see one method definition with Union{}, we can record that the method is the only possible match for that slot. This, in essence, permits creating a rule for dispatch that a TypeVar lower bound must be strictly a supertype of Union{}, but this creates it at the function level, instead of expecting the user to add it to every TypeVar they use to define methods. This also lets us improve the error message for these cases (generally they should error to avoid polluting the inference result), since we can be assured this method will be called, and not result in an ambiguous MethodError instead! Reverts the functional change of #46000
Based on the new morespecific rule for Union{} and method definitions of the specific form `f(..., Type{Union{}}, Vararg)`. If a method definition exists with that specific form, the intersection visitor will ignore all intersections that have that as their only result, saving significant effort when working with lookups involving `Type{<:T}` (which usually ended up mostly ambiguous anyways). Fixes: #33780 This pattern turns out to have still to been making package loading slow. We could keep adding methods following the ambiguity pattern #46000 for the couple specific functions that need it (constructor, eltype, IteratorEltype, IteratorSize, and maybe a couple others) so the internals can detect those and optimize functions that have that method pair. But it seems somewhat odd, convoluted, and non-obvious behavior there. Instead, this breaks all ambiguities in which Union{} is present explicitly in favor of the method with Union{}. This means that when computing method matches, as soon as we see one method definition with Union{}, we can record that the method is the only possible match for that slot. This, in essence, permits creating a rule for dispatch that a TypeVar lower bound must be strictly a supertype of Union{}, but this creates it at the function level, instead of expecting the user to add it to every TypeVar they use to define methods. This also lets us improve the error message for these cases (generally they should error to avoid polluting the inference result), since we can be assured this method will be called, and not result in an ambiguous MethodError instead! Reverts the functional change of #46000
Based on the new morespecific rule for Union{} and method definitions of the specific form `f(..., Type{Union{}}, Vararg)`. If a method definition exists with that specific form, the intersection visitor will ignore all intersections that have that as their only result, saving significant effort when working with lookups involving `Type{<:T}` (which usually ended up mostly ambiguous anyways). Fixes: #33780 This pattern turns out to have still to been making package loading slow. We could keep adding methods following the ambiguity pattern #46000 for the couple specific functions that need it (constructor, eltype, IteratorEltype, IteratorSize, and maybe a couple others) so the internals can detect those and optimize functions that have that method pair. But it seems somewhat odd, convoluted, and non-obvious behavior there. Instead, this breaks all ambiguities in which Union{} is present explicitly in favor of the method with Union{}. This means that when computing method matches, as soon as we see one method definition with Union{}, we can record that the method is the only possible match for that slot. This, in essence, permits creating a rule for dispatch that a TypeVar lower bound must be strictly a supertype of Union{}, but this creates it at the function level, instead of expecting the user to add it to every TypeVar they use to define methods. This also lets us improve the error message for these cases (generally they should error to avoid polluting the inference result), since we can be assured this method will be called, and not result in an ambiguous MethodError instead! Reverts the functional change of #46000
Based on the new morespecific rule for Union{} and method definitions of the specific form `f(..., Type{Union{}}, Vararg)`. If a method definition exists with that specific form, the intersection visitor will ignore all intersections that have that as their only result, saving significant effort when working with lookups involving `Type{<:T}` (which usually ended up mostly ambiguous anyways). Fixes: #33780 This pattern turns out to have still to been making package loading slow. We could keep adding methods following the ambiguity pattern #46000 for the couple specific functions that need it (constructor, eltype, IteratorEltype, IteratorSize, and maybe a couple others) so the internals can detect those and optimize functions that have that method pair. But it seems somewhat odd, convoluted, and non-obvious behavior there. Instead, this breaks all ambiguities in which Union{} is present explicitly in favor of the method with Union{}. This means that when computing method matches, as soon as we see one method definition with Union{}, we can record that the method is the only possible match for that slot. This, in essence, permits creating a rule for dispatch that a TypeVar lower bound must be strictly a supertype of Union{}, but this creates it at the function level, instead of expecting the user to add it to every TypeVar they use to define methods. This also lets us improve the error message for these cases (generally they should error to avoid polluting the inference result), since we can be assured this method will be called, and not result in an ambiguous MethodError instead! Reverts the functional change of #46000
I found that there is a pitfall with dispatching on custom parameterized types due to
Type{Union{}} <: Type{<:MyType}
(JuliaArrays/StaticArrays.jl#685). Borrowing @c42f's example, the problem is that:For a concrete type like
Int
, the solution is to writeType{Int}
. However, it is tricky for parameterized types. Possible workarounds are:Use
foo(::Type{T}) where {P, MyType{P} <: T <: MyType}
: However, this interferes with the inliner Unpirate Union{}[] JuliaArrays/StaticArrays.jl#685 (comment) possibly due to unbound type parameters (see also Remove some spuriouswhere T
from SA constructors JuliaArrays/StaticArrays.jl#665, lower bound interferes with typevar value inference #30713).Separate dispatches as in
foo(::Type{MyType})
andfoo(::Type{MyType{P}}) where P
. Unpirate Union{}[] JuliaArrays/StaticArrays.jl#685 uses this solution.Write a helper function like
Type₁(U) = Type{T} where {P, U{P} <: T <: U}
and then definefoo(::Type₁(MyType))
.However, none of the above solutions works well when there are many parameters. It may be useful to have a generalization of approach 3 (say)
Type₊(U::UnionAll)
that works with an arbitrary number of parameters. Or maybe there is a better solution?The text was updated successfully, but these errors were encountered: