In-place functions with arrays and scalars

For a function that returns scalars and arrays, what is good practice for the mutating version? Below, x and y are scalars and A and B are arrays.

function f(...)
  x = ...
  y = ...
  A = ...
  B = ...
  return x, A, y B
end

This doesn’t work:

function f!(x, A, y, B, ...)
  x = ...
  y = ...
  A = ...
  B = ...
end

I could do like this, but it feels ugly:

function f!(A, B, ...)
  x = ...
  y = ...
  A = ...
  B = ...
  return x, y
end

Last version makes sense to me.

1 Like

I think this leads to arranging function arguments according to their type instead of semantics. Otherwise you have to keep track of which of the arguments are mutating and which are not.

x, A, y, B = f(...)
# Mutating:
f!(x, A, y, B, ...)
# Non-mutating:
x, y = f!(A, B, ...)

Incremental computation is also affected:

x_t, A_t, y_t, B_t = f(....)
x += x_t
A += A_t
y += y_t
B += B_t

Instead of

f!(x, A, y, B, ...)

for a function

function f!(x, A, y, B, ...)
  x += ...
  y += ...
  A += ...
  B += ...
end

Has everyone just settled for what @kristoffer.carlsson suggested or are there other solutions?

xref, yref = Ref(x), Ref(y)
f!(xref, X, yref, Y, ...)

There is no way to mutate a value. You can only mutate things stored inside the value. Consequently you can mutate [5] but not 5. There’s nothing Julia-specific about that statement.

Also note that none of the version above mutates A and B.

1 Like

I defend always the following: ! is a licence to modify, not an obligation.
Then

ynew = f!(y)

can be used as idiom for generic code which does not stop working when replacing a MVector by an SVector. Unfortunately that is not strictly followed in Base.

1 Like

Modifying multiple arguments is usually a strong signal that they should be organized into a composite type (struct) or at least a NamedTuple.

2 Likes