6

I have an immutable structure with four objects defined as follows:

struct FltFric
    muS::Array{Float64, 2}
    muD::Array{Float64, 2}
    Dc::Float64
    W::Array{Float64, 2}
end

muS = repmat([0.6], 100, 1) # Coefficient of static friction
muD = repmat([0.5], 100, 1) # Coefficient of dynamic friction
Dc = 0.1                     # Critical slip distance

FltFriction = FltFric(muS, muD, Dc, zeros(size(muS)))

I am modifying the values of FltFric.muS as follows:

FltFriction.muS[1:20] = 100

This works fine. But when I try to modify the value of W

FltFriction.W = (FltFriction.muS - FltFriction.muD)./(FltFriction.Dc)

This gives me an error: type FltFric is immutable.

Why does the first statement not give error while the second one does? If the type is immutable, both statements should give an error. What is the difference between the two assignments?

I know that I can circumvent the problem by typing mutable struct, but I don't understand the difference in my two assignments.

Prithvi Thakur
  • 263
  • 2
  • 10
  • 1
    I had trouble with this when I started out with Julia too, and in fact, asked pretty much the same question on StackOverflow [here](https://stackoverflow.com/questions/27371153/understanding-immutable-composite-types-with-fields-of-mutable-types-in-julia). But the answer you have gotten here is good. Arrays in Julia are mutable, and they don't suddenly become immutable just because you put them in an immutable type. The fact that your type is immutable means *only* that you can't change the entirety of one your fields. You can still change the innards of a field though if it is mutable. – Colin T Bowers May 03 '18 at 22:33

1 Answers1

12

I am not a Julia expert, but I think this is a more general question.

In the first assignment, you're modifying certain elements of the list FltFriction.muS. This is fine since although the struct is immutable, the list referred to by .muS is mutable. In other words, you're mutating the list by changing its elements, rather than mutating the struct.

In the second assignment, you're trying to replace the entire list .W in one fell swoop. In this case you're trying to mutate the struct directly, replacing one of its elements. For this reason, the second assignment fails while the first one succeeds.

I'm speculating here, but I suspect that if you tried to do the second assignment like so:

FltFriction.W[1:end] = ...

Then you would be fine, since you're mutating the list instead of the struct.

As pointed out by a commenter (see below), in Julia there is a "more idiomatic (and more performant)" way to do this correctly and without mutating the struct itself by using the in-place assignment operator (neat!):

FltFriction.W .= (FltFriction.muS - FltFriction.muD)./FltFriction.Dc

bjarchi
  • 180
  • 1
  • 8
  • 3
    This is correct. And the `...` could be e.g. `1:end`. A more idiomatic (and more performant) way to write this specific case would be `FltFriction.W .= (FltFriction.muS - FltFriction.muD)./FltFriction.Dc` which would fuse all the dotted operators, and use inplace assignment (`.=`) to modify the `W` array. – fredrikekre May 03 '18 at 21:35
  • Thanks! I've edited my answer to include this information, with a reference/credit to your comment (well, to the comments). I've only had a chance to use Julia for personal projects - I'm happy that I managed to migrate my work from Matlab to numPy/sciPy - but the more I see of it the more I like. – bjarchi May 03 '18 at 22:45
  • 4
    Note that `FltFriction.W .= (FltFriction.muS .- FltFriction.muD)./FltFriction.Dc` or `@. FltFriction.W = (FltFriction.muS - FltFriction.muD)/FltFriction.Dc` would be more performant because missing the dot means it fuses in two parts instead of one. – Chris Rackauckas May 04 '18 at 05:51
  • 1
    It's easy to miss a dot. In complicated expressions, the `@.` macro is very convenient. Also, `W[1:end]` is more conveniently written as `W[:]`. – DNF May 04 '18 at 07:17
  • Thanks, I get it. But in this case, when should we actually use mutable struct? Especially when I am only dealing with arrays and matrices, is there any need ever to use mutable struct? – Prithvi Thakur May 04 '18 at 15:59
  • 1
    I'm not a Julia expert, but since nobody else has responded I'll share my experience from, e.g., Python. In general, it's desirable to use an immutable data type when possible. This prevents inadvertent/unintentional modification of the underlying structure (such as a bug modifying struct instead of member) - leading to an exception rather than unexpected behavior. It also may determine whether the type is hashable or not; this matters in Python for e.g. keys of dictionaries. I can add this to the answer if Julia folks agree or add information. – bjarchi May 07 '18 at 21:26