28

When I create some array A and assign it to B

A = [1:10]
B = A

I can modify A and the change reflects in B

A[1] = 42
# B[1] is now 42

But if I do that with scalar variables, the change doesn't propagate:

a = 1
b = a
a = 2
# b remains being 1

I can even mix the things up and transform the vector to a scalar, and the change doesn't propagate:

A = [1:10]
B = A
A = 0
# B remains being 1,2,...,10

What exactly does the = operator does? When I want to copy variables and modify the old ones preserving the integrity of the new variables, when should I use b = copy(a) over just b=a?

RedPointyJackson
  • 675
  • 8
  • 17
  • Based on your anticipation of the results reflected from the question, R language may give you least surprise. – colinfang Feb 06 '16 at 05:09

2 Answers2

38

The confusion stems from this: assignment and mutation are not the same thing.

Assignment. Assignment looks like x = ... – what's left of the = is an identifier, i.e. a variable name. Assignment changes which object the variable x refers to (this is called a variable binding). It does not mutate any objects at all.

Mutation. There are two typical ways to mutate something in Julia: x.f = ... – what's left of the = is a field access expression; x[i] = ... – what's left of the = is an indexing expression. Currently, field mutation is fundamental – that syntax can only mean that you are mutating a structure by changing its field. This may change. Array mutation syntax is not fundamental – x[i] = y means setindex!(x, y, i) and you can either add methods to setindex! or locally change which generic function setindex!. Actual array assignment is a builtin – a function implemented in C (and for which we know how to generate corresponding LLVM code).

Mutation changes the values of objects; it doesn't change any variable bindings. After doing either of the above, the variable x still refers to the same object it did before; that object may have different contents, however. In particular, if that object is accessible from some other scope – say the function that called one doing the mutation – then the changed value will be visible there. But no bindings have changed – all bindings in all scopes still refer to the same objects.

You'll note that in this explanation I never once talked about mutability or immutability. That's because it has nothing to do with any of this – mutable and immutable objects have exactly the same semantics when it comes to assignment, argument passing, etc. The only difference is that if you try to do x.f = ... when x is immutable, you will get an error.

StefanKarpinski
  • 32,404
  • 10
  • 86
  • 111
  • Thank you for your complete answer. As I have understanded you, all my `b=a` operations are assignments, so in all cases `b` should refer to `a`. What I am missing? – RedPointyJackson Oct 08 '15 at 06:47
  • When you do `A[1] = 42` that's mutation, not assignment. – StefanKarpinski Oct 08 '15 at 16:52
  • Ok, I undestand that. But when I do `b=a`, it should be assignment because it's an operation in the `x = ...` style, right? So now I have `b` 'pointed' to `a`, and all changes in `a` should reflect whenever I evaluate b, don't they? – RedPointyJackson Oct 09 '15 at 12:49
  • 1
    Yes, that's correct and all of this behavior is completely in line with that. If you do `a = b` then any change to `b` also affects `a`. If the value bound to `b` is an immutable value like `42` then you can't mutate it anyway, so there's no way to tell if it was copied or referenced. – StefanKarpinski Oct 10 '15 at 04:47
  • 1
    There is a [section about this in the Julia FAQ](https://docs.julialang.org/en/v1/manual/faq/#I-passed-an-argument-x-to-a-function,-modified-it-inside-that-function,-but-on-the-outside,-the-variable-x-is-still-unchanged.-Why?-1) – Mankka Mar 13 '19 at 08:19
  • @StefanKarpinski Do you think it can be understood from stack & heap ? Hope your reply... Thx again – BertKing Apr 30 '19 at 03:01
  • No, stack and heap are implementation details with no additional explanatory power. Sometimes immutable values are stored on the heap, sometimes mutable ones are stored on the stack. And assignment vs mutation definitely doesn't relate. – StefanKarpinski Apr 30 '19 at 10:36
-4

This behavior is similar to Java. A and B are variables that can hold either a "plain" data type, such as an integer, float etc, or a references (aka pointers) to a more complex data structure. In contrast to Java, Julia handles many non-abstract types as "plain" data.

You can test with isbits(A) whether your variable A holds a bit value, or contains a reference to another data object. In the first case B=A will copy every bit from A to a new memory allocation for B, otherwise, only the reference to the object will be copied.

Also play around with pointer_from_objref(A).

  • 3
    This answer is misleading at best: mutability vs immutability is a red herring. Mutable and immutable values have identical semantics in both languages – both have reference semantics. That's because neither Julia nor Java have mutable value types, like C's structs, which do have observably different semantics. – StefanKarpinski Oct 09 '15 at 09:09