Skip to content
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 f(instance::MyType) or warn on additional method, for f(x) = f(typeof(x)) #34985

Open
goretkin opened this issue Mar 3, 2020 · 5 comments
Labels
kind:breaking This change will break code kind:speculative Whether the change will be implemented is speculative

Comments

@goretkin
Copy link
Contributor

goretkin commented Mar 3, 2020

I suggest either removing any definition other than f(::Type{...}), or warning if someone adds a new method that isn't f(::Type{...}).

After reading

julia/base/generator.jl

Lines 83 to 87 in ddf904c

julia> Base.IteratorSize(1:5)
Base.HasShape{1}()
julia> Base.IteratorSize((2,3))
Base.HasLength()
and seeing the examples, I ended up implementing IteratorSize(::MyType)=SizeUnknown(), which worked in some situations, and then broke when some library called IteratorSize(MyType) and got the default (HasLength()). At first I suspected something was wrong with [how I understand] Generators, so I went to go check another example of a SizeUnknown() iterator used in a Generator, and funny enough I found the same bug in IterTools: https://github.com/JuliaCollections/IterTools.jl/pull/70/files#diff-bd068feabf42c1d394ba76bc98a4d738L934

If there weren't these parallel definitions, or if there were a warning if someone adds a new method IteratorSize(::not_Type{....}) (so to speak), then this bug wouldn't be possible. The bugs are pretty small, but so is the convenience of not writing out typeof. It seems like removing the parallel definitions would be consistent with what's been done for e.g. fieldnames (see related issues below).

Here is a [possibly not exhaustive] list of such parallel definitions in Base:

$ grep -r . -n -E -e '([a-zA-Z]+)\(.+\)\s*=\s*\1\(.*typeof.*\)'
./abstractarray.jl:124:keytype(a::AbstractArray) = keytype(typeof(a))
./abstractarray.jl:129:valtype(a::AbstractArray) = valtype(typeof(a))
./abstractarray.jl:153:elsize(A::AbstractArray) = elsize(typeof(A))
./abstractdict.jl:257:keytype(a::AbstractDict) = keytype(typeof(a))
./abstractdict.jl:271:valtype(a::AbstractDict) = valtype(typeof(a))
./abstractset.jl:289:The definition `hasfastin(x) = hasfastin(typeof(x))` is provided for convenience so that instances
./abstractset.jl:295:hasfastin(x) = hasfastin(typeof(x))
./array.jl:110:`eltype(x) = eltype(typeof(x))` is provided for convenience so that instances can be passed
./array.jl:125:eltype(x) = eltype(typeof(x))
./broadcast.jl:216:argtype(bc::Broadcasted) = argtype(typeof(bc))
./generator.jl:90:IteratorSize(x) = IteratorSize(typeof(x))
./generator.jl:123:IteratorEltype(x) = IteratorEltype(typeof(x))
./indices.jl:94:IndexStyle(A::AbstractArray) = IndexStyle(typeof(A))
./multidimensional.jl:323:    ndims(R::CartesianIndices) = ndims(typeof(R))
./reflection.jl:1195:parentmodule(f::Function) = parentmodule(typeof(f))
./subarray.jl:332:iscontiguous(A::SubArray) = iscontiguous(typeof(A))
./traits.jl:9:OrderStyle(instance) = OrderStyle(typeof(instance))
./traits.jl:19:ArithmeticStyle(instance) = ArithmeticStyle(typeof(instance))
./traits.jl:59:RangeStepStyle(instance) = RangeStepStyle(typeof(instance))

I'd say the case for removing these methods for the trait interfaces (IteratorSize, IteratorEltype, ...) is pretty strong. And that the case for the convenience of eltype(my_vector) is pretty strong too, so I'm not sure.

Related:
#34788 (comment)
#22350

@kimikage
Copy link
Contributor

kimikage commented Mar 4, 2020

I agree with the idea, but I think the impact is too large.

@JeffBezanson JeffBezanson added the kind:speculative Whether the change will be implemented is speculative label Mar 4, 2020
@goretkin
Copy link
Contributor Author

I agree with the idea, but I think the impact is too large.

Preventing incorrect trait definitions should not have any impact in bug-free code, and it would find bugs like the one referenced.

Though I don't think there's any mechanism for preventing it at the moment. It might be the job for a linter.

@kimikage
Copy link
Contributor

What are the criteria for correct or incorrect?

@goretkin
Copy link
Contributor Author

goretkin commented Nov 26, 2020 via email

@goretkin
Copy link
Contributor Author

A related reply on discourse from @StefanKarpinski :

Lack of formal interfaces and restrictions on how one can extend generic functions like max. If you were only allowed to extend it by specializing the two-argument method then you would have been lead to the right solution here. The cost would be that there may be situations where one need to directly specialize the more general method and if we disallowed that it could be problematic (limit what people can do or require a way to let someone say “no, I really want to do this”).

@brenhinkeller brenhinkeller added the kind:breaking This change will break code label Nov 20, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind:breaking This change will break code kind:speculative Whether the change will be implemented is speculative
Projects
None yet
Development

No branches or pull requests

4 participants