Consider the following example where I get this error even though I provide init
julia> foo(x, y, a_set) = x - sum(y[i] for i in a_set, init = zero(eltype(y)))
foo (generic function with 1 method)
julia> foo(3, ones(4), Set{Int}())
ERROR: ArgumentError: reducing over an empty collection is not allowed; consider supplying `init` to the reducer
Stacktrace:
[1] _empty_reduce_error()
@ Base .\reduce.jl:311
[2] mapreduce_empty(f::Function, op::Base.BottomRF{typeof(Base.add_sum)}, T::Type)
@ Base .\reduce.jl:313
[3] reduce_empty(op::Base.MappingRF{var"#foo##0#foo##1"{…}, Base.BottomRF{…}}, ::Type{Tuple{…}})
@ Base .\reduce.jl:350
[4] reduce_empty_iter
@ .\reduce.jl:373 [inlined]
[5] reduce_empty_iter
@ .\reduce.jl:372 [inlined]
[6] foldl_impl
@ .\reduce.jl:41 [inlined]
[7] mapfoldl_impl
@ .\reduce.jl:36 [inlined]
[8] mapfoldl
@ .\reduce.jl:167 [inlined]
[9] mapreduce
@ .\reduce.jl:299 [inlined]
[10] sum
@ .\reduce.jl:524 [inlined]
[11] sum
@ .\reduce.jl:553 [inlined]
[12] foo(x::Int64, y::Vector{Float64}, a_set::Set{Int64})
@ Main .\REPL[3]:1
[13] top-level scope
@ REPL[4]:1
Some type information was truncated. Use `show(err)` to see complete types.
I don’t understand why this does not work. It works if I collect the iterator first but the whole point of a generator is to avoid this collection. Is there no way around? I feel like I’m missing something.
You are missing a semicolon in sum. Using a comma leads to init not being parsed as a keyword argument.
julia> foo(x, y, a_set) = x - sum(y[i] for i in a_set; init = zero(eltype(y)))
foo (generic function with 1 method)
julia> foo(3, ones(4), Set{Int}())
3.0
Someone actually just ran into basically the same thing yesterday on Zulip, so I’ll just copy and slightly adapt what I said there
This is unfortunately just a nasty, confusing cornercase where our keyword argument syntax overlaps with the generator syntax.
One way you can protect yourself from this is always wrap a generator in parentheses, i.e.
sum((y[i] for i in a_set), init = zero(eltype(y)))
and it’s also good practice to always separate kwargs with a ;, so you’d then get
sum((y[i] for i in a_set); init = zero(eltype(y)))
P.S.: This isn’t really that relevant to the problem, but it might be good to know that you actually don’t need to use generators here to avoid allocations, you can also use the 2-argument function version of sum, i.e.
While it is correct that oftentimes you don’t need a semicolon, I think it is good style to nonetheless use a semicolon evertime. It takes a short while to get used to but then you will never have to think about it again and it actually saves you in instances like this. Personally I always use the semicolon and it recommend in 2 of the largest style guides