When defining a vararg method, a zero argument method is implicitly created:
julia> foo(args...) = sum([args...])
foo (generic function with 1 method)
julia> foo()
ERROR: MethodError: no method matching zero(::Type{Any})
Closest candidates are:
zero(::Type{Union{Missing, T}}) where T at missing.jl:104
zero(::Type{Dates.Time}) at /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.5/Dates/src/types.jl:406
zero(::Type{Dates.DateTime}) at /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.5/Dates/src/types.jl:404
...
Stacktrace:
[1] zero(::Type{Any}) at ./missing.jl:105
[2] reduce_empty(::typeof(+), ::Type{Any}) at ./reduce.jl:311
[3] reduce_empty(::typeof(Base.add_sum), ::Type{Any}) at ./reduce.jl:320
[4] mapreduce_empty(::typeof(identity), ::Function, ::Type{T} where T) at ./reduce.jl:343
[5] reduce_empty(::Base.MappingRF{typeof(identity),typeof(Base.add_sum)}, ::Type{Any}) at ./reduce.jl:329
[6] reduce_empty_iter at ./reduce.jl:355 [inlined]
[7] mapreduce_empty_iter(::Function, ::Function, ::Array{Any,1}, ::Base.HasEltype) at ./reduce.jl:351
[8] _mapreduce(::typeof(identity), ::typeof(Base.add_sum), ::IndexLinear, ::Array{Any,1}) at ./reduce.jl:400
[9] _mapreduce_dim at ./reducedim.jl:318 [inlined]
[10] #mapreduce#620 at ./reducedim.jl:310 [inlined]
[11] mapreduce at ./reducedim.jl:310 [inlined]
[12] _sum at ./reducedim.jl:727 [inlined]
[13] _sum at ./reducedim.jl:726 [inlined]
[14] #sum#627 at ./reducedim.jl:722 [inlined]
[15] sum at ./reducedim.jl:722 [inlined]
[16] foo() at ./REPL[1]:1
[17] top-level scope at REPL[2]:1
In my experience, this is usually not what you want. In order to avoid this, you have to do something like this:
julia> foo(x, y...) = sum([x, y...])
foo (generic function with 1 method)
julia> foo()
ERROR: MethodError: no method matching foo()
Closest candidates are:
foo(::Any, ::Any...) at REPL[1]:1
Another downside to implicitly defining the zero argument method is that it is easy to accidentally engage in type piracy when you are extending a method:
struct A
x::Int
end
Base.max(as::A...) = max(map(a -> a.x, as)...)
This defines a Base.max()
method, which is type piracy. Not to mention the unintended zero argument method causes a stack overflow:
julia> max(A(2), A(3))
3
julia> max(A(2))
2
julia> max()
ERROR: StackOverflowError
I’ve listed here a few costs to implicitly defining a zero argument method when defining a vararg function. Are there any benefits?