Running the code below, I expected Vector{Float64} for both cases. Is there an explanation for why
I don’t get this for the g function?

function ff(y)
function a(x)
x>y ? x : ""
end
return a
end
g = ff(5)
f(x) = x>0 ? x : ""
println(typeof([Float64(f(x)) for x in zeros(0)]))
println(typeof([Float64(g(x)) for x in zeros(0)]))

The reason that this matters is that the sum function returns 0 for empty Float64 arrays, but throws an exception for Vector{Any}.

julia> Float64("")
ERROR: MethodError: no method matching Float64(::String)
Closest candidates are:
(::Type{T})(::AbstractChar) where T<:Union{AbstractChar, Number} at C:\Users\woclass\.julia\juliaup\julia-1.7.3+0~x64\share\julia\base\char.jl:50
(::Type{T})(::Base.TwicePrecision) where T<:Number at C:\Users\woclass\.julia\juliaup\julia-1.7.3+0~x64\share\julia\base\twiceprecision.jl:255
(::Type{T})(::Complex) where T<:Real at C:\Users\woclass\.julia\juliaup\julia-1.7.3+0~x64\share\julia\base\complex.jl:44
...
Stacktrace:
[1] top-level scope
@ REPL[32]:1
julia> parse(Float64, "")
ERROR: ArgumentError: cannot parse "" as Float64
Stacktrace:
[1] _parse_failure(T::Type, s::String, startpos::Int64, endpos::Int64) (repeats 2 times)
@ Base .\parse.jl:373
[2] #tryparse_internal#452
@ .\parse.jl:369 [inlined]
[3] tryparse_internal
@ .\parse.jl:367 [inlined]
[4] #parse#453
@ .\parse.jl:379 [inlined]
[5] parse(::Type{Float64}, s::String)
@ Base .\parse.jl:379
[6] top-level scope
@ REPL[33]:1

Maybe ArgumentError is better than MethodError.

julia> [Float64(f(x)) for x in zeros(0)]
Float64[]
julia> [Float64(g(x)) for x in zeros(0)]
Any[]
julia> [parse(Float64,f(x)) for x in zeros(0)]
Float64[]
julia> [parse(Float64,g(x)) for x in zeros(0)]
Float64[]

f is implicitly const, and g is explicitly not const. The compiler doesn’t assume g won’t be reassigned in the middle of an array comprehension because it could be by a side effect.

Thank you, this explains it partially, but isn’t the result of the Float64 function always a Float64?

I tried a small modification and this actually works:

function ff(y)
function a(x)
x>y ? x : ""
end
return a
end
float64(x)::Float64 = Float64(x)
g = ff(5)
f(x) = x>0 ? x : ""
println(typeof([Float64(f(x)) for x in zeros(0)]))
println(typeof([float64(g(x)) for x in zeros(0)]))

So why would the compiler assume that float64 returns Float64, but not the Float64() function itself?

It’s perfectly reasonable to expect so, but type constructors are just functions, and a method can return anything. See for yourself: struct A end; Float64(::A) = 0; typeof(Float64(A())). This is rare, though. The more useful possibility is methods that throw errors, which are inferred with a “return type” of Union{}.

This was just a stripped down version of my own code to illustrate the problem. If you imagine that I might know that x always has positive values or that I am okay with having an error thrown if x is negative. But thanks for your reply.