Array comprehension: Can someone explain this typeof result?

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}.

I don’t know why, but …

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.

1 Like

In this case, the best strategy is to use Float64[...] in the comprehension.

julia> println(typeof(Float64[g(x) for x in zeros(0)]))
Vector{Float64}
2 Likes

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{}.

Okay, thank you. I will just assume that there is a good reason for this and use one of the solutions in this thread.

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.