Okay, I have a reproducible example
function toyreduce_impl(f, op, itr, ifirst, ilast, blksize)
A = itr.x
if ifirst == ilast
a1 = A[ifirst]
if a1 === missing #|| false
return nothing
else
return Some(Base.mapreduce_first(f, op, a1))
end
elseif ifirst + blksize > ilast
# sequential portion
local ai
i = ifirst
while i <= ilast
ai = A[i]
ai === missing || break
i += 1
end
i > ilast && return nothing
a1 = ai::eltype(itr)
i += 1
while i <= ilast
ai = A[i]
ai === missing || break
i += 1
end
i > ilast && return Some(Base.mapreduce_first(f, op, a1))
a2 = ai::eltype(itr)
i += 1
v = op(f(a1), f(a2))
for i = i:ilast
ai = A[i]
if ai !== missing
v = op(v, f(ai))
end
end
return Some(v)
else
# pairwise portion
imid = (ifirst + ilast) >> 1
v1 = toyreduce_impl(f, op, itr, ifirst, imid, blksize)
v2 = toyreduce_impl(f, op, itr, imid+1, ilast, blksize)
if v1 === nothing && v2 === nothing
return nothing
elseif v1 === nothing
return v2
elseif v2 === nothing
return v1
else
return Some(op(something(v1), something(v2)))
end
end
end
function toyreduce_impl_bad(f, op, itr, ifirst, ilast, blksize)
A = itr.x
if ifirst == ilast
a1 = A[ifirst]
if a1 === missing || false
return nothing
else
return Some(Base.mapreduce_first(f, op, a1))
end
elseif ifirst + blksize > ilast
# sequential portion
local ai
i = ifirst
while i <= ilast
ai = A[i]
ai === missing || break
i += 1
end
i > ilast && return nothing
a1 = ai::eltype(itr)
i += 1
while i <= ilast
ai = A[i]
ai === missing || break
i += 1
end
i > ilast && return Some(Base.mapreduce_first(f, op, a1))
a2 = ai::eltype(itr)
i += 1
v = op(f(a1), f(a2))
for i = i:ilast
ai = A[i]
if ai !== missing
v = op(v, f(ai))
end
end
return Some(v)
else
# pairwise portion
imid = (ifirst + ilast) >> 1
v1 = toyreduce_impl_bad(f, op, itr, ifirst, imid, blksize)
v2 = toyreduce_impl_bad(f, op, itr, imid+1, ilast, blksize)
if v1 === nothing && v2 === nothing
return nothing
elseif v1 === nothing
return v2
elseif v2 === nothing
return v1
else
return Some(op(something(v1), something(v2)))
end
end
end
julia> x = map(1:100) do _
rand() < .2 ? missing : rand()
end;
julia> sx = skipmissing(x);
julia> Missings.toyreduce_impl(identity, +, sx, 1, 100, 100)
Some(42.487664183551566)
julia> @code_warntype Missings.toyreduce_impl(identity, +, sx, 1, 100, 100)
If you run the above code on 1.4 you will get good type inference. The result will say Union{Nothing, Some{Float64}}
somewhere.
However if you run
julia> @code_warntype Missings.toyreduce_impl_bad(identity, +, sx, 1, 100, 100)
You will get Any
in some places and Some{_A} where _A
(so Some(Any)
) in other places.
However the only difference between toyreduce_impl
and toyreduce_impl_bad
is the addition of a || false
on the 4th line of the function, which shouldn’t affect any behavior of the code.
cc @nalimilan on this. Let me know if I should file an issue with Julia base.
Edit: It seems like if you delete the last branch at the end. the else
, things infer fine in toyreduce_impl_bad
. I think that Julia is having trouble determining the types of objects that aren’t reached.