Why is Type Mutability of Variables Useful?


#1

I understand why values of a variable should be changeable (unless declared const). It’s the whole point of having a variable.

Curious: Are there many code patterns where one would want the type of an already instantiated variable to be changeable? Especially because ‘Any’ is a type that could hold a variety of types if absolutely needed, and one could create a new variable with a different type and initialize it based on the contents of an existing instantiated variable when need be?

I am asking because I think (not sure) that type mutability of global variables creates all sorts of issues for the compiler; and I think possibly also for the user. I don’t think I ever write code where I want the type to switch around. I think I would rather trigger an error if this happens.


#2

It’s important for interactive usage. Imagine a jupyter notebook setup where you think in cells although you have one big global scope. It happens regularily that you defined some a=2 in one cell and maybe a=3.2 in another cell. Do you want Julia to throw an error here (Int -> Float64)? That would be more than annoying.


#3

However, AFAIR, there was the idea to allow for type annotations in global scope at some point such that this would error

a::Int = 2 # new type annotation feature here: make a's type a compile time constant.
a = 3.2 # error because 3.2 isn't Int

#4

There are two questions here: one is about globals, the other is about variable types in general.

For the first one (globals), I would not assume that having no facility for declaring variables to have a fixed type but mutable values is something essential, it’s just that no one got around to doing this. Most programmers just avoid globals, or if necessary, use a mutable container with a fixed type.

Regarding the other question (why variable types can be mutable): writing quick & dirty experimental code without worrying about types is a great asset for Julia. One can then tidy it up later, or conclude that the compiler can handle the type variation.


#5

As Tamas mentioned, this is a multifaceted question. Let’s just consider the case of local variables. Try the following exercise at the REPL:

julia> function summer(A)
           s = 0
           for a in A
               s += a
           end
           return s
       end

julia> @code_warntype summer([1,2,3])
# output suppressed

julia> @code_warntype summer([1.0,2.0,3.0])
# output suppressed

You’ll see that the second one has Union{Int,Float64} variables, which happens because the accumulator is initialized with an Int but adding an Int to a Float64 produces a Float64. Thanks to Julia’s compiler in 1.0, the price you you pay is relatively small:

julia> @btime summer($x)
  7.205 ns (0 allocations: 0 bytes)
6.0

julia> @btime fast_summer($x)
  3.513 ns (0 allocations: 0 bytes)
6.0

where fast_summer initializes s with zero(eltype(A)), and where x = [1.0, 2.0, 3.0].

The flexibility you gain from this is huge. There are circumstances where it’s difficult or impossible to initialize properly: for example, suppose you defined addition rules where ::Int8 + ::Int8 gave you a Int16, then ::Int16 + ::Int8 gave you an Int32, and so on; if you insisted on never changing the type, figuring out the right initialization could be quite challenging. You can handle this with Any variables, but the fact that Julia can sometimes be more specific is key to getting good performance in challenging situations and on “sloppy” code. Even for very experienced programmers, it’s sometimes much easier to be sloppy than to be “correct,” so handling this well contributes significantly to programmer efficiency.


#6

thank you. it’s beginning to dawn on me.

I can see why it is convenient for REPL experiments, and how type-constraining globals will make it better.

tim—the summer() has the convenient init on s, but your zero(eltype(A)) would have done the job easily. for more complicated examples that you sketch (where the type of a variable that I need to initialize has to be computed, rather than simply picked off the type of another variable), it can indeed get hairy. however, I don’t think this kind of hairy would be too common a situation. I would think it would be rare and obscure.

does julia support compiler “pragmas”? @code_warntype (new?) shows in red the kind of warnings that I wish the julia compiler would give me and other noobies by default, perhaps even as error depending on pragma setting.

regards,

/iaw


#7

Given that the compiler is already sophisticated enough to handle this kind of thing, there is little reason to do this globally—it would only make Julia a more brittle language. But there are places where you might want to insist on inferrability, see https://github.com/JuliaLang/julia/issues/10980.