40
$\begingroup$

Usually in programming languages, function arguments are normal local variables, which can be modified.

Is this not true in Mathematica?

In[94]:= TTT[x_] := Block[{},
   x += 2
   ];

In[95]:= TTT[3]

During evaluation of In[95]:= AddTo::rvalue: 3 is not a variable with a value, so its value cannot be changed. >>

Out[95]= 3 += 2

Is it possible to simulate normal argument behavior somehow?

$\endgroup$
8
  • 1
    $\begingroup$ Relevant(?): mathematica.stackexchange.com/a/2677 $\endgroup$ Jan 14, 2013 at 19:14
  • $\begingroup$ @OleksandrR. I think so! And yet another one of a similar kind. I'll probably later add these links to my answer. $\endgroup$ Jan 14, 2013 at 19:20
  • $\begingroup$ @LeonidShifrin ah, I'd forgotten about that answer of yours. Very good solution... nice to see it again! $\endgroup$ Jan 14, 2013 at 19:44
  • $\begingroup$ Also related: (3269), (4126), (9078), (13662) $\endgroup$
    – Mr.Wizard
    Jan 15, 2013 at 4:41
  • 2
    $\begingroup$ Like this? SetAttributes[rasher, HoldFirst]; rasher[x_] := x = x + 1; a = 2; rasher[a]; a $\endgroup$
    – ciao
    Feb 14, 2014 at 3:48

4 Answers 4

64
$\begingroup$

Intro

I will treat your question in a somewhat broader context of parameter-passing semantics in Mathematica in general. Many points of confusion here come from analogies and comparisons with more traditional languages, and it is important to realize that Mathematica uses entirely different (from most other languages) mechanisms for parameter-passing. Realizing this is not helped by the fact that there are significant syntactic similarities with other languages, however.

Parameter - passing semantics in Mathematica : pass-by-value via code injection

Parameter-passing semantics in Mathematica is different from many other languages. Parameters are passed by value, but if you dig deeper, functions are really rules, and parameters are simply injected into the body of a function, right before the body is evaluated. Depending on whether or not the function in question holds a particular argument, what is injected into its body is either the argument verbatim, or its value (meaning that the argument is first evaluated and only then injected, and then the body evaluates).

Whether or not the expressions passed as arguments can be modified inside a function depends on whether or not they represent an L-value (meaning they can be assigned a value, e.g. via Set operator). I have a rather long section in my book which has a detailed discussion on that, but, to put the story short, for the following function:

f[x_]:=x=1

this will assign to a:

Clear[a];
f[a]

while this will result in an error:

a=2;
f[a]

because in the first case, the symbol a is injected into the r.h.s. (it is of course first evaluated, but since it does not have any value, it evaluates trivially, to itself), while in the second, it already has a value, so it's immutable value 2 is injected.

Note however, that this "injection" parameter-passing mechanism is highly unusual, from the point of view of traditional languages. In essence, parameter-passing acts as a code-generation device, assembling full function's code in a macro-like fashion from code pieces passed as parameters and the body which has "placeholders" and acts like a macro. One can say that in Mathematica, run-time code generation happens at each function invocation.

Emulating pass-by-reference via Hold-attributes

When functions are given Hold-attributes, they can emulate pass-by-reference semantics since they inject their passed arguments into their bodies verbatim, without first evaluating them. Therefore, giving f the attribute HoldFirst (for example), will lead to assignments in both cases.

ClearAll[f];
SetAttributes[f,HoldFirst];
f[x_]:= x = x + 5;

a = 5;
f[a];
a

(* 10 *)

f[a];
a

(* 15 *)

This is however only an emulation, since there are no pointers in Mathematica. One representative aspect of this emulation is that you can not "dereferece" a symbol holding a value. Nevertheless, for many tasks, such an emulation is sufficient.

Getting the behavior similar to other languages (such as C)

Finally, you can easily simulate the effect of local variables by assigning your passed parameters to Module or Block-local variables:

TTT[x_] := Block[{xl=x},
   xl += 2
];

But in this case, the changes in those local variables will not affect the values of the parameters (just like this would be for function's parameters in C, for example). This behavior is probably the closest analog of how passed parameters belong in say C, where they are copied on the stack and become function's local variables.

The topic of name conflicts for passed parameters and local variables in actually more complex, and some of the associated behavior may be not very easy to understand at first. Mathematica documentation has an extensive coverage of these topics. I also discussed this topic in some more (compared to this answer) details here.

Summary

Parameter-passing in Mathematica is very different from that of many more traditional languages, in terms of core mechanisms involved (rules, run-time code generation). However, the syntax and partly behavior may look pretty close to what we have in other languages. It is important to understand the core mechanisms behind Mathematica's parameter-passing scheme, to better understand the differences and avoid unwanted surprises.

$\endgroup$
26
  • 3
    $\begingroup$ Congrats on hitting 30k ! $\endgroup$
    – Artes
    Jan 14, 2013 at 18:04
  • $\begingroup$ @Artes Thanks man! $\endgroup$ Jan 14, 2013 at 18:05
  • 1
    $\begingroup$ Welcome to the 30k club! The club just became 100% more awesome :) $\endgroup$
    – rm -rf
    Jan 14, 2013 at 18:35
  • $\begingroup$ @rm-rf Thanks, buddy! I thought you and Mr.Wizard would be too lonely there without me :) $\endgroup$ Jan 14, 2013 at 18:42
  • $\begingroup$ Mr.Wizard is now the lonely one in his exclusive 40k club =) Alas, time is too tight to be able to keep pace with him like I used to (and the same goes for you, Sz and Heike) $\endgroup$
    – rm -rf
    Jan 14, 2013 at 18:45
9
$\begingroup$

Short answer: You need to set the attribute HoldAll on your function to prevent the variable to be modified from being changed into its value before it's substituted into the assignment. See long answer below.

AppendTo has this attribute (see Attributes[AppendTo]).


In other languages, pass by reference is used for two purposes:

  1. avoiding copying large data structures

  2. enabling the function to modify its arguments

Number (1) is not necessary in Mathematica. Mathematica handles data structures in a copy-on-write like manner so data structures are not duplicated when references from several locations or when passed to functions. For more information see Share.

Number (2) is what you are asking about in your question. To enable a function to modify its argument in Mathematica, it must not evaluate its arguments by default.

Suppose we'd like to create a function that increments a variable by 1:

increment[x_] := x = x+1

If we evaluate a=2; increment[a] now, this is what happens:

  1. a gets evaluated to 2
  2. increment[2] gets evaluated to 2 = 2+1
  3. 2+1 gets evaluated to 3
  4. We end up with 2=3 which yields an error as an integer can't be assigned to.

You can see this by looking at the output of TracePrint[increment[a]].

Clearly, we need to prevent a from evaluating to 2 before it gets substituted into x=x+1. This can be achieved by setting the attribute HoldAll on increment.

If a function has the attribute HoldAll, its arguments will not be evaluated before the function itself. Se we need:

SetAttributes[increment, HoldAll]
increment[x_] := x = x + 1

Now the evaluation sequence for increment[a] is:

  1. increment[a] --> a = a+1
  2. a+1 --> 2+1
  3. 2+1 --> 3
  4. a = 3

... and a is set to 3, i.e. incremented by 1!


$\endgroup$
1
  • $\begingroup$ Thanks. I ran into this problem when trying to update a list passed as a parameter. Somehow I had managed to avoid the idea of the HoldAll attribute until today :-) The error message (and on-line help) kind of made it clear what the problem was. I had to come here to find a fix. $\endgroup$ May 8, 2020 at 14:51
7
$\begingroup$

First off, there is no use for your Block as you don't specify which variables you're localizing

Secondly, MMA will evaluate the inner expression x before it evaluates the outer one TTT[x] unless you specify otherwise. That's why it replaces the x with the 3 inside the function.

Try

SetAttributes[TTT, HoldFirst];
TTT[x_]:= (x+=2);

Finally, in your particular example, you tell MMA to use the number 3 inside the function, with TTT[3]. No programming language I know allows you to redefine integers. Instead, attach a symbol to 3, then try the following:

x = 3;
TTT[x];
x == 5
(* True *)

Edit: It occurred to me that what you may have wanted when calling TTT[3] is something similar to pass-by-value, but creating a local symbol with a unique name is probably the best way to do this in MMA (usually with a With).

$\endgroup$
2
$\begingroup$

I can't remember where I first saw this, but here is another way:

variable = "Old Value";
f[Dynamic[arg_]] := arg = "New Value";
variable
f[Dynamic[variable]];
variable

which works because Dynamic holds its first argument, as explained in the other answers. From the documentation for Dynamic,

Dynamic has attribute HoldFirst, so that expr is not evaluated until its value is needed for display.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.