Multiple type declarations with if-else

I understand the proper practice is to pre-allocate outputs, but why wouldn’t the following work?

function f()
    if 1 == 1
        x::Float64 = 1.0
    else
        x::Float64 = 2.0
    end
    return x
end

This returns the following error message:

syntax: multiple type declarations for "x"

yet I don’t think I am really declaring types multiple times, since x is only created once.

1 Like

I don’t know why you’re not allowed to do that, but if you want, you can do x = (1 == 1 ? Float64(1) : Int64(2)), since the function doesn’t have type stability anyway

There’s a distinction between the assignment and the declaration of the name x. Yes, you’ll only ever assign to x once, but that one x name is the same throughout the entire function (including before the assignment, amusingly enough). While the program flow will only ever see one of the two branches, the syntax applies to the entire function.

3 Likes

I think the format x::Float64 is basically saying you want to declare a variable of type x as Float64. If you change the code to:

function f()
    if 1 == 1
        x = Float64(1.0)
    else
        x = Float64(2.0)
    end
    return x
end

That works, and it’s doing what you expect the original function to do. i.e. X is of type Float64 but I don’t specifically declare it.

Yes you (because you are human) can inspect your original code and know what you expect to happen. The compiler apparently just sees you declaring X twice and doesn’t want to play that game.
At a guess, the compiler programmers probably felt that if you are declaring the same variable twice that it was likely in error and rather than make a best guess at what you wanted to do, decided an error would be better so you could look at the code and determine what you really wanted to do.

It’s also worth noting that this is not pre-allocating an output. That advice applies to arrays; here you’re working with a single immutable number that won’t need allocation at all.

You almost never need to declare types on variables — it almost never has a speed impact.

2 Likes

I would say here that you are correct in that you don’t declare types on a variable for performance. You may however declare types on a variable to avoid coding pitfalls, i.e. you can’t accidentally change the data type if it was declared.

1 Like

Thank you all for the swift replies! Yes I was declaring types mostly to avoid running into mistakes when using the package ForwardDiff, and came across this problem when I was creating arrays with type Real (bad for speed, but that’s what the package requires).
So is it correct to infer from @mbauman’s answer that types are determined when names are first put to use, rather then when variables are created by associating the names to values?

Types are attached to objects. The number 2.0, for example, has the type Float64. Julia’s compiler will go through your function and realize, for example that the name x is only ever used to refer to things of type Float64 and thus it can apply a whole host of optimizations (like not needing to look up what + method to use, for example).

You don’t need to use Array{Real} to work with ForwardDiff and can work directly with arrays of Duals. For example, you really shouldn’t specify ::Array{Real} in your function argument lists, but it’s hard to say what you may be doing here.

If you like this very verbose style where you declare local variables and types, you can always do this:

function f(a)
    local x::Float64
    if 1 == 1
        x = 1.0
    else
        x = 2.0
    end
    return x
end

Now x will always be a Float64 in the scope of f:

function g()
    local x::Float64
    if 1 == 1
        x = 10
    else
        x = 2.0
    end
    return x
end

julia> g()
10.0
4 Likes