-
-
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
Deprecate conversion to Int for "non-obvious" index types #10458
Conversation
@@ -451,3 +451,27 @@ function push!(A) | |||
depwarn("push!(A) has been deprecated", :push!) | |||
A | |||
end | |||
|
|||
typealias DepIdxTypes Union(FloatingPoint,MathConst,Rational) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like you don't actually use this alias?
👍 |
deprecate `to_index(::Real)` in favor of `to_index(::Integer)` remove tests that exercise floating point indexing minor doc change, add NEWS item
ada64ce
to
173bd57
Compare
Also, perhaps you should leave a note in deprecated.jl to "tighten up" the signatures of get/setindex once the Real |
I think I like this idea, but I'd ask to hold off on merging it. It may indeed be that this is the right direction to go in, but I'd like to make sure that folks have thought this through very carefully. In particular, I strongly advise against some vague notion of "cleanliness" or "minimalism" in making this decision: the much more important goal is handling ambiguities that arise for package authors when making argument 1 more specialized but argument 2 less specialized. Related: #10440. In other words, I think we have to consider how do/will package authors overload # Avoid ambiguity warning with blah blah
getindex(A::MyArray, ::Int) = ...
# Avoid ambiguity warning with ...
getindex(A::MyArray, ...)
# Avoid ...
...
# Now here is finally the definition I really wanted:
getindex(A::MyArray, ...) Some of those "interception" definitions will be unavoidable, but requiring a whole heap of them gets pretty annoying. As one example of where I think this gets complicated, for an getindex(A::InterpolationArray, x::Number...) That works fine if Base defines a method getindex(A::AbstractArray, x::Number...) because argument 1 is more specialized, and the remaining arguments are "equally specialized", so this one definition suffices to control dispatch. But it will be ambiguous against getindex(A::AbstractArray, x::Int...) because the arguments have opposite specialization behavior. You could solve that particular ambiguity with a getindex(A::InterpolationArray, x::Int...) but having getindex(A::AbstractArray, x::Real...) is probably the worst of all worlds. What makes this problem somewhat harder is that there is another goal that seems, in practice, to be partially competing. It would be really nice if most package authors only had to define indexing for scalar types, and that Base handles "hard stuff" like this: getindex(A::AbstractArray, x::Union(ScalarType, AbstractVector)...) # mixed scalar/vector indexing
getindex(A::AbstractArray, x::Union(ScalarType, CartesianIndex)...) # fancy scalar indexing, coming soon automatically for most array types. To disambiguate those, you need to have getindex(A::AbstractArray, x::ScalarType...) defined. If we make In other words, I think this is a complicated problem 😄. I've only just begun to think about this seriously, but perhaps someone out there has a clear analysis or argument that points the way to the fewest ambiguities for package authors, without making Base overly fussy for users? If so, please do share 😄. Again, any change we make here is guaranteed to cause ambiguity warnings for package authors, no matter what kind of change we make. So, let's make the change just once in the 0.4 cycle, because (in my opinion) ambiguities are in practice among the more annoying problems to cope with. |
I should add that my basic intuition is that we want to migrate Base to the extremes: for any methods for a specific array type, the type of the indexes should also be as narrowly-defined as is reasonable. For any method that we'd like to define as a fallback for all |
I'm not sure there should be any methods for indexing AbstractArray with
all scalars.
|
I think it's worth considering whether we should define getindex(A::AbstractArray, indexes::Union(ScalarType,AbstractVector)...) because that would mean users wouldn't have to figure out how to implement indexing over a region---most users would only have to define scalar indexing and the rest would follow automatically from this fallback. But if we do that, we probably need to at least specify a "stub" method getindex(A::AbstractArray, indexes::ScalarType...) = error("getindex not defined for ", typeof(A)) to avoid ambiguities with getindex(A::MyArray, indexes::Union(ScalarType, SomeOtherType)...). Having thought about this a little more: I think the design principle is to minimize the number of nodes in the method graph that possess branches. |
In particular, the definition will soon be getindex(A::AbstractArray, indexes::Union(ScalarType,AbstractVector)...) = slice(A, indexes...) (or |
Very sensible and thoughtful, as usual, @timholy. Since packages (and base) have stabilized on Which is why I actually think the course of action here (doing the deprecation within I also really like the idea of giving AbstractArrays fancy indexing behavior by default, only requiring |
I initially assumed the scope of this PR was bigger, which is part of why I was a bit concerned. I agree that doing the deprecation via I'm in the middle of creating a PR that changes scalar indexing to use I'm also trying to implement the fallback fancy indexing methods for a broader class of array types. This might not be very useful if 0.4 returns views from indexing, but to me that's beginning to look a bit unlikely, so perhaps we should at least fix up our current It's an interesting idea to dispatch to functions not named |
definitely hit a chord with me---it seems like it might save a lot of duplicated code in trying to handle situations in which the number of indexes does not match the dimensionality of the array. I would be tempted to make the linear part optional, to be called if the user set the trait I don't think we have a good way to dispatch splats based on the number of arguments, and this little test: f1(x...) = f2(x)
@noinline f2{N,T}(x::NTuple{N,T}) = N
function callf1(n, x...)
s = 0
for i = 1:n
s += f1(x...)
end
s
end
callf1(1, 2, 5)
@allocated callf1(10^6, 2, 5) does seem to suggest that life will not be all roses if we use a tuple as an intermediary. (At least, with our current tuples.) So it almost seems we'd have to establish a new convention, for example that the new functions Would introducing |
@timholy I thought that this was the right balance between what we want (constraining the index types to integers for I am skeptical as well to defining one catchall fallback for |
Yep, I'm fine with merging this; if you're ready, I say go for it. My not yet submitted PR is based on this one; I'll need to think a bit more about whether that PR is a direction we want to go in.
The ambiguity warnings that would arise here are not the "useless" category like There was a "baby" version of this in #10314, where we had to delete a class of conflicting methods, |
Deprecate conversion to Int for "non-obvious" index types
@ScottPJones moving the discussion here. I know I was a pushing force for this, but I'm waffling on this a bit more. There are two reasons I liked this change.
I still think 1 is a pretty good motivation. But I'm coming around to the fact that |
Regarding the difference between indexing with 42 and 42.0: I guess eg when
interpolating integer data, indexing with 42 could give an integer and with
42.0 a float. So somewhat different effects at least.
|
…ng i/j with i÷j (see JuliaLang#10458)
…ng i/j with i÷j (see JuliaLang#10458)
…ng i/j with i÷j (see JuliaLang#10458)
…ng i/j with i÷j (see JuliaLang#10458)
…ng i/j with i÷j (see JuliaLang#10458)
to_index(::Real)
in favor ofto_index(::Integer)
.Int
.Closes #10154