11
$\begingroup$

This question already has an answer here:

I haven't seen this question specifically addressed before in this site, although some hints and traces have been given, for instance, here:

So, my question is: How can/should I specify which arguments of a given user-defined function have to be treated as passed by reference or by value? Preferably, this should be done in the definition of the function. Clear reliable instructions are also desirable.

I want to pass some variables by reference to a function, because I want the function to modify the value of that variable (i.e., the function has to act or work on that variable, and not on a local copy of that variable that would vanish when the function ends). I do not feel comfortable using 'global variables' (which is the default scope for any variable in Mathematica, as far as I understand). I prefer to apply a classic approach: any variable or 'element' that is going to be either used or modified by a function, has to be 'passed' to that function via its argument list, either by reference or by value. In this way, I can better monitor what is happening and less unexpected interactions or behaviours can occur (of course, this reasoning is nothing new to all those who usually program in languages such of C, etc.).

I have already read something about using Hold, Unevaluated, etc. I am just posing this question to have it specifically addressed and treated in this Q&A forum, so that complete general answers can be given.

If this specific subject is already addressed elsewhere, I would appreciate to be informed about.

As a MWE, I can give this:

myfunc[a_, b_] := {
   a = 2*a;
   b = -b;
   };
vara = 5;
varb = 6;
myfunc[vara, varb];
{vara, varb}

Actually, it is an incomplete NOT working example. In this example, I would like

  • a to be treated as a passed-by-reference variable (so that vara should be actually changed at the end of the main routine), while

  • b being treated as a passed-by-value variable (so that b = -b should not have any actual influence on the value of varb).

How can I specify this in Mathematica?

Currently, this example returns the following errors: "Set: Cannot assign to raw object 5" and "Set: Cannot assign to raw object 6" because the function is trying to modify something which is interpreted by Mathematica as a constant, not a variable (I mean the function is getting raw numbers 5 and 6 as inputs, I guess).

BONUS: How does it affect to the result the fact that the variables being involved are defined either with = or :=? I guess = should preferably be used when dealing with variables that are expected to contain values (temporary results of partial computations, generally speaking), and := for defining functions and so on.

$\endgroup$

marked as duplicate by Leonid Shifrin, MarcoB, WReach, Young, J. M. is away Sep 22 '17 at 16:40

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • 3
    $\begingroup$ I am not sure why you think that the posts you linked do not address this question. Use a Hold* attribute and pass the variable name. $\endgroup$ – Szabolcs Sep 22 '17 at 8:10
  • $\begingroup$ @Szabolcs Those questions are asking for solutions for concrete cases (i.e., passing a list, etc.). I was asking in a more general way. I still don't know whether Hold is a general solution that works in all cases, or whether there are some issues to have into account (I mean, Hold has many different uses; it was not meant to be created just to deal with pass-by-reference variables). $\endgroup$ – Vicent Sep 22 '17 at 8:14
  • 3
    $\begingroup$ The answer is that pass by value and pass by reference don't make sense for Mathematica because Mathematica is a term rewriting system, and thus fundamentally different from anything else you are likely to be familiar with. It does imitate several aspects of other languages, thus we talk about "functions" and "function calls", but it is actually based on entirely different principles. I tried to explain this in my answer. Let me know if it's clear enough. $\endgroup$ – Szabolcs Sep 22 '17 at 9:00
  • 2
    $\begingroup$ Another way to look at it is that M is strictly "pass by value", thus conceptually all expressions get continually copied and we are never allowed to have any references to one copy or another. The implementation uses copy-on-write though, so performance isn't reduced. $\endgroup$ – Szabolcs Sep 22 '17 at 9:02
  • $\begingroup$ @Vincent See my comment to Szabolcs' answer. $\endgroup$ – Sascha Sep 22 '17 at 9:20
20
$\begingroup$

You asked for a general explanation instead of just focusing on specific application examples, so here it goes ...

The concepts of "pass by reference" and "pass by value" that you may know from languages like C do not apply very well to Mathematica. Do not try to think in this framework.

The right question is not "how to pass by reference/value", but how to achieve certain things that you would use these features for in other languages.

Please start by reading the first five sections of this tutorial:

Mathematica is unique as a programming language because it is a term rewriting system.* It doesn't even have functions in the sense that C or Java do. It simply does pattern matching and substitution. When you write

f[x_] := x^2

then f[x_] is simply a pattern, comparable to regular expressions. "Making a definition" is just telling the system to examine every expression it encounters against this pattern, and if it matches, transform it to what's on the right-hand-side of :=.

In your example, you try to treat b as a variable, and want b=-b to have no effect. This makes no sense in Mathematica because b is not a variable, but a (sub-)pattern name—a way to refer to a certain part of the expression that matched a pattern. Whatever part of the expression matched b will be directly substituted into the RHS of the definition. Thus f[2] transforms directly to 2^2 and the name x is immediately gone. This is why you see the Set::setraw error: you end up with expressions like 6 = -6.

A common use of pass by reference is to have functions that can modify "their arguments". What does "their argument" mean here? There are subtle differences in this between languages. In many other languages, it means a direct reference (e.g. pointer) to one instance of an in-memory data structure. Mathematica does not give you direct access to its data structures so that it can manage copying them on its own (and implement the copy-on-write optimization). What you can do instead is to pass a variable name (i.e. symbol) to your function, and then let it modify the definition of that symbol. Since symbols immediately evaluate to the value that is assigned to them, you will need to carefully control evaluation to be able to achieve this. This is explained in the tutorial I linked to above.

The standard way to do this is to use the HoldAll attribute which prevents the evaluation of arguments:

a = 1;

SetAttributes[f2, HoldAll]

f1[a]
(* f1[1] *)

f2[a]
(* f2[a] *)

Now if you match f2 using a pattern like f2[x_], then x will refer to a and not 1. This enables you to manipulate the symbol a in your function instead of its value. For example, you can assign a new value to this symbol. But you may also evaluate it, and work with its value if you wish. Example:

f1[x_] := {Hold[x], x}

f2[x_] := {Hold[x], x}

f1[a]
(* {Hold[1], 1} *)

f2[a]
(* {Hold[a], 1} *)

A function that increments the value of the variable passed to it:

SetAttributes[inc, HoldAll]
inc[x_] := (x = x + 1)

inc[a]
(* 2 *)

a
(* 2 *)

How does it work?

  • When the system sees inc[a], it would normally start by evaluaating a. But since inc has the HoldAll attribute, it skips this step

  • The pattern inc[x_] matches inc[a], so the transformation rule inc[x_] :> (x = x+1) is applied. Thus inc[a] gets transformed into a = a+1.

  • The system proceeds with the evaluation of the expression a = a+1, which causes the definition of a to change. At this point the expression no longer contains inc, so we can forget about the symbol completely.

Understanding evaluation control, and the difference between functions like Hold or Unevaluated is often difficult for newcomers to Mathematica. After you have read the tutorial on the Evaluation of Expressions, I suggest you also take a look at this one:


* The only other general purpose term rewriting language I know of is Pure (and its predecessor Q).

$\endgroup$
  • 3
    $\begingroup$ I'd think one should mention that writing good code without any weird corner cases is difficult in Mathematica, especially when diverging from "best practices" i.e. writing non-pure function that modify some of their arguments. All in all I think writing code like this is a bad idea (in almost any language). $\endgroup$ – Sascha Sep 22 '17 at 9:19
8
$\begingroup$

You need:

SetAttributes[myfunc, HoldAll]

You then have:

myfunc[a_, b_] := {a = 2*a;
   b = -b;};
vara = 5;
varb = 6;
SetAttributes[myfunc, HoldAll]
myfunc[vara, varb];
{vara, varb}
{10, -6}

Look for HoldAll, HoldFirst and HoldRest in the documentation and this site.

You can do this:

myfunc[a_, bIn_] := Block[{b},
   {a = 2*a,
    b = -bIn}];
vara = 5;
varb = 6;
SetAttributes[myfunc, HoldFirst]
myfunc[vara, varb];
{vara, varb}
{10, 6}
$\endgroup$
  • $\begingroup$ The variable b was meant to be passed by value in my example. $\endgroup$ – Vicent Sep 22 '17 at 8:16
  • $\begingroup$ @Vicent, see update (and documentation :-) $\endgroup$ – user21 Sep 22 '17 at 8:22

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