Julia's assignment behavior differs from Fortran?

You can try it and see. Note that this example is different as there is no mutation of any objects, only assignment. The array example involves mutation of arrays.

2 Likes

I’m afraid I don’t understand the difference how x=y is mutation but xOld=xNew is assignment. Does the equal (=) command mean something different for arrays than for numbers?

Numbers always “copy” in julia. There is no concept of mutating a number because numbers are not mutable structs.

1 Like

Think more along the lines of eager evaluation then lazy evaluation. The expression on the right hand side is evaluated before it is assigned to the label on the left hand side.

2 Likes

There’s never any mutation with a plain x=y thing. It’s just naming and values. Names on the left, values on the right.

Just like how we use natural languages (like English), a particular thing can have no names, one name, or many names. The names you use for a particular thing can change over time and can be different depending upon which room you’re in (scope).

5 Likes

You are still thinking of variables as boxes, please look at the post I linked above.

No. xNew is bound to the value resulting from xOld+1 which is 2. xNew is not a closure which will always recompute its value when the variables used to define it are changed. What happened previously happened because you were dealing with a mutable object (a Vector/Array). You used two variables/labels to refer to the same object, and you changed inside the mutable object. So it reflected in the two variables because they are just names for the same object that now is different than it previously was.

3 Likes

They’re the same — they’re both assignment. It’s the y[1] = 1.0 and y[1] = 2.0 bit that’s mutation. Without that, arrays and scalars behave the same. Example:

function example_no_mut_scalar()
    xOld = 1
    xNew = 2
    xOld = xNew
    println("xOld = ",xOld,", xNew = ",xNew)
    return
end

function example_no_mut_array()
    xOld = [1]
    xNew = [2]
    xOld = xNew
    println("xOld = ",xOld,", xNew = ",xNew)
    return
end

They work the same way:

julia> example_no_mut_scalar()
xOld = 2, xNew = 2

julia> example_no_mut_array()
xOld = [2], xNew = [2]

The example you were surprised by involves mutation of an array, which cannot be done in the scalar case because integers are not mutable.

function example_mut_scalar()
    xOld = 1
    xNew = 2
    xOld = xNew
    xNew[1] = 1 # not actually possible
    println("xOld = ",xOld,", xNew = ",xNew)
    return
end

function example_mut_array()
    xOld = [1]
    xNew = [2]
    xOld = xNew
    xNew[1] = 1
    println("xOld = ",xOld,", xNew = ",xNew)
    return
end

These behave differently, but only in that mutating a scalar is an error:

julia> example_mut_scalar()
ERROR: MethodError: no method matching setindex!(::Int64, ::Int64, ::Int64)

julia> example_mut_array()
xOld = [1], xNew = [1]

Bottom line: no matter what kind of object you’re dealing with when you do x = y then x points at the same object as y.

10 Likes

Yes and no. As @StefanKarpinski pointed out, it is not the type that matters, but if it is x = y or x[i] = y. x = y is defined by the language it (creates a new name x if there was not one and) just binds the value to that name in that scope. x[1] = 2 is a completely different animal, it lowered to (setindex!(x, 2, 1); 2) (i.e., call the function setindex! for the appropriate type of x and return the value passed to it, in this example it is 2), you can see more typing ?setindex! in the Julia REPL.

1 Like

To clarify, x = y means the same thing for all values regardless of their kind. However x = y and x[i] = y are totally different.

4 Likes

I appreciate all of these detailed explanations, guys, but I’m still not confident that I know what’s going to happen in a given program. I think I’m going to have to program, print out, and pray.

2 Likes

I have been there. Fortran is so simple that we have to learn even about what they are talking about in these answers. Mutable? Immutable? Labels? x[i]=1 is a function call? It is fun if you take it the easy way.

2 Likes

I’m not a programmer, I’m a physicist writing a program.

So I’m hoping to use the program like a walking stick for support, then the stick turns into a Swiss army knife, and everyone says that “those are better than sticks!”. Which may be true, but…

3 Likes

Counterpoint, having

y = [1, 2]
x = y

not perform a copy is super helpful and imo pretty simple. It means that you can write a program that re-names variables as freely as you like, with mutation, and not worry about the overhead of copying variables. Its a really good convenience.

4 Likes

Yeah, from a performance standpoint, setting up x as just a name pointing to y is better than copying over the entire contents of y into x. But, I wonder, why ever rename a variable at all? If you have the name y already, what’s the use of having x as another name for the same thing? Now you just have a redundant new name x that gets changed by commands that don’t mention x in them at all.

1 Like

It only gets changed if it is mutable. It is not a risk if you are using immutable objects like numbers. Also, the meaning of some data may change inside a function scope. You may have a current and old variables in which data flows to current and then to old, you may have a data and a buffer variables with the same kind of object and then you make changes to the buffer and make it the new data, and then store the old data as the new buffer. These are all cases I had in my real code and that benefited for this behaviour.

Agreed. The point is if the advantages are worth the efforts.

Is plotting? Automatic differentiation? Automatic access to all blas without having to deal with compilation flags? Easy parallelization? Distribution of the code to every platform easily? Easy access to all the famous Optimization packages?

1 Like

I go by Matt, Matthew, and (very very rarely) Dr. Bauman. All three names refer to the same thing — myself. You tell Matt to do something, and my mom would describe it as Matthew doing something, and someone who is trying to be overly formal and doesn’t really know me might describe it as Dr. Bauman doing something. Yes, this is “spooky action” where telling one name to change something about itself actually affects all three names at once without referencing all three names — but we’re very used to it!

This is the key — the name is not the thing. Mutations happen to the thing. The name is just how we describe the thing to one another — and depending upon the context different names might be useful.

13 Likes

I find this discussion very interesting. I have the impression that it is hard for someone that has not programmed in Fortran for a long time to understand the issues we Fortran programmers have (not with Julia in particular, with other languages in general, probably). The thing is that Fortran is so restricted in its purposes that a lot of the choices one has in other languages are at least not default behaviors in Fortran, and the default behavior is most generally a good one for numerical computations. That is why one can be a completely ignorant in most aspects of computer programming and still produce good quality and fast Fortran programs. And that is why Fortran is still around.

When you, experts, answer our questions, many times you simply do not imagine how ignorant we are about even the terminology you use, even if we are (and I could give examples of that) the developers of important numerical packages that you use in your every day life.

4 Likes

I am still trying to understand what mutable means, I googled it and still couldn’t quite get it.

BTW, my last large-scale numerical simulation programming effort (before the one I’m doing this year) used a shiny new programming language you may have heard of, called Fortran 90. (It was rough adapting over from Fortran 77, but I got used to it.)

(Actually, that doesn’t count the stuff I was doing in Mathematica. But Mathematica doesn’t really let you look under the hood at all, which is why I’m here.)

1 Like

Maybe this will help. Note that y1 is just a pointer to the M object. So is y2, consequently when you modify the inside of y2 using the name y2, since the name y1 points to the same thing, y1 also looks like its modified.

In contrast, you can’t modify x2.x because its not a mutable struct. The “two names for the same object” logic doesn’t work.

julia> mutable struct M
       x
       end

julia> struct P 
       x 
       end

julia> y1 = M(1);

julia> y2 = y1
M(1)

julia> y2.x = 5;

julia> y1
M(5)

julia> x1 = P(1)
P(1)

julia> x2 = x1;

julia> x2.x = 5
ERROR: setfield! immutable struct of type P cannot be changed