How to pass a symbol name to a function with any of the Hold attributes?



Given a function with the attribute HoldFirst, HoldAll or similar, and a variable, list, how can I pass the variable's name to the function without the variable being evaluated prematurely? So it is "list" that is to be passed to the function (e.g. AppendTo) instead of list.

The following method fails, as AppendTo holds the evaluation of Symbol @ "list", and since Symbol[...] is not a valid variable for which a value can be assigned, an error message appears:

list = {1, 2, 3};
AppendTo[Symbol @ "list", 1];
Set::write: Tag Symbol in Symbol[list] is Protected.>>

{1, 2, 3}

I should mention, that for certain reasons the obvious direct assignment below is not possible, as I must pass the list as a string:

list = {1, 2, 3};    
list = Append[list, 2]; (* this works, but is not what I am looking for *)

{1, 2, 3, 2}

The next approach also fails, as it issues an error message while returning the correct result:

list = {1, 2, 3};
held = AppendTo[Evaluate @ ToExpression["list", InputForm, Hold], 3];
ReleaseHold @ held;
AppendTo::rvalue: Hold[list] is not a variable with a value, so its value cannot be changed.>>

{1, 2, 3, 3}

The final approach reveals that ReleaseHold removes the outermost Hold, but leaves the innermost intact.

list = {1, 2, 3};
held = Hold @ AppendTo[Evaluate@ToExpression["list", InputForm, Hold], 4];
ReleaseHold @ held
AppendTo::rvalue: Hold[list] is not a variable with a value, so its value cannot be changed.>>

AppendTo[Hold[list], 4]

István Zachar

Posted 2012-03-06T21:39:16.347

Reputation: 44 135



How about this:

list = {1, 2, 3};
ToExpression["list", InputForm, Hold] /. Hold[v_] :> AppendTo[v, 3]

{1, 2, 3, 3}


{1, 2, 3, 3}


Posted 2012-03-06T21:39:16.347

Reputation: 62 787

3ToExpression["list", InputForm, Hold] can be written as ToHeldExpression@"list" – masterxilo – 2016-07-22T13:18:46.190

This works as well for a list of variables, and seems to be a bit more straightforward than celtschk's answer. I am doing some more testing. – István Zachar – 2012-03-06T22:58:51.583


Not to detract from the existing answers (particularly @WReach's suggestion, which was the same solution that came to my mind as I read your question, and which I will use here), but you may find it easier to define your own references rather than using strings. (In fact, I wouldn't necessarily recommend an approach based on building Mathematica expressions out of strings as a good first choice in general.) The advantage of using your own operator is that it will be easier to get rid of it when you don't want it any more, and no complex syntax is needed to actually use it.

First, let us define the reference object (which is essentially just a wrapper):

Attributes[referenceTo] = {HoldAll, Listable};

referenceTo[str_String] :=
  referenceTo[Evaluate@ToExpression[str, InputForm, Hold]];

referenceTo@Hold[sym_Symbol] := referenceTo[sym];

referenceTo /: f_[argsPre___, ref : referenceTo[str_String], argsPost___] := 
 f[argsPre, Evaluate[ref], argsPost]

We can either use this directly, or on strings. For example:

{a, b, c} = Range[3]; (* make some definitions to prove it works *)

referenceTo[{"a", b, Hold[c]}]
    (* -> {referenceTo[a], referenceTo[b], referenceTo[c]} *)

So, while very simple, the behaviour of the wrapper is probably sufficient for most purposes in terms of creating the references in the first place (and, of course, the range of situations in which it is recognized can readily be expanded if necessary). Now we tell it when and how to dereference the symbols it wraps:

$dereferencingFunctions = {AppendTo, ReleaseHold};
referenceTo /: f_[argsPre___, referenceTo[sym_Symbol], argsPost___] /; 
 MemberQ[$dereferencingFunctions, f] :=
  f[argsPre, sym, argsPost]

You can now manipulate $dereferencingFunctions to switch this behaviour on and off for different functions as required and avoid the need for manual dereferencing as part of each call. Thus,

list = {elem1};
AppendTo[referenceTo[list], elem2];
stuff = elem3;
AppendTo[referenceTo["list"], referenceTo[stuff]];
(* a rather odd way to use it, but possibly useful: *)
AppendTo @@ referenceTo[{"list", elem4}];

evaluates correctly and without producing any messages, and gives:

    (* -> {elem1, elem2, elem3, elem4} *)

Or, if you prefer:

    (* -> {elem1, elem2, elem3, elem4} *)

In my opinion, despite its extreme simplicity, this approach manages to be much more practical than using strings directly.

Oleksandr R.

Posted 2012-03-06T21:39:16.347

Reputation: 22 073

Would it be possible to extend this approach, so that this would work as expected x=5; referenceTo["x"] = 15; x? In other words, so that variables could be set by reference as well? – Ajasja – 2012-05-22T12:54:33.360

@Ajasja: Yes, just write AppendTo[$dereferencingFunctions, Set] and it works automatically. (This won't work for list assignments, like {q, referenceTo[r], referenceTo["s"]} = {1, 2, 3}, though. Some changes would be needed to support that.) – Oleksandr R. – 2012-05-22T22:04:34.900

Thanks! I actually tried that, but it was giving me a warning that went away upon restarting a fresh kernel. Is it safe to use this with SetDelayed as well? – Ajasja – 2012-05-23T08:00:45.950

@Ajasja: yes, there shouldn't be any evaluation leaks as the arguments are passed right through with the symbols dereferenced. The approach is so simple that I'm sure you'll be able to find some corner cases, but for typical usage I think it is robust enough (at least, better than using strings, which was the main point here). – Oleksandr R. – 2012-05-24T00:29:45.173

While I agree with you that a proper wrapper for referencing would be the best solution, at the moment I still try to solve my issues with structural replacements. Your solution seems robust, but I wouldn't say it is simple. At least not at 2am :) – István Zachar – 2012-03-07T01:49:59.887


Here is yet another possibility (which, in a way, combines some of the suggestions given already): define a new scoping construct, most similar to Function, to perform the task you need:

SetAttributes[strFunction, HoldAll];
SyntaxInformation[strFunction] =
     {"ArgumentsPattern" -> {_, _}, "LocalVariables" -> {"Solve", {1, 1}}};
strFunction[var_, expr_] :=
      ToExpression[strvar, InputForm,
             Unevaluated[expr] /.HoldPattern[var] :> held, 

You can use it as:

list = {1, 2, 3};
strFunction[l, AppendTo[l, 1]]["list"]

Mapping on a list of such names is also quite easy in this approach:

Map[strFunction[l, AppendTo[l, 1]], list-of-symbol-names]

You can also add type-checks, generalize to functions with more than one variable, etc.

Leonid Shifrin

Posted 2012-03-06T21:39:16.347

Reputation: 108 027

@Ajasja Ok, I see - there is indeed an evaluation leek. Sorry, you spotted it correctly. Should change Unevaluated[var] :> held to HoldPattern[var] :> held - will edit in a moment. Thanks for spotting this - I am getting older :). Will delete my erroneous comments. – Leonid Shifrin – 2012-05-22T11:59:47.283


Injector pattern:

list = {1, 2, 3};

MakeExpression["list"] /. _[sym_] :> AppendTo[sym, 4]

Function (here using the Null syntax trick):

Function[, AppendTo[#, 4], HoldAll] @@ MakeExpression["list"]


Posted 2012-03-06T21:39:16.347

Reputation: 259 163

1I had a different name for this pattern, and am also a big fan and frequent user of it, but Injector seems better. +1. – Leonid Shifrin – 2012-03-07T06:20:42.027

The syntax highlighter definitely doesn't like your code, Mr.W (I do). – István Zachar – 2012-03-07T10:59:46.477

@Istvan it took me a little bit to figure out what you mean. Normally I have "Missing arguments" highlighting turned off. Since this is AFAIK a perfectly valid use of MakeExpression I suggest that you modify your SyntaxInformation accordingly if you like to keep that setting turned on. You can also use ToHeldExpression instead if you prefer. – Mr.Wizard – 2012-03-07T13:55:04.300

It is not just the missing second argument for MakeExpression, but 1) since there is no & for the #, it is coloured purple, and 2) the comma in Function is coloured as well. Not that I mind any of these. – István Zachar – 2012-03-07T14:07:14.763


This works:


The mechanism is as follows:

  • The block temporarily undefines list so that after Symbol["list"] is evaluated, evaluation stops.
  • However, the AppendTo shall not be evaluated as long as list is undefined, therefore it is wrapped in Hold.
  • Symbol shall be evaluated, therefore it may not be inside Hold. For this reason, it is injected into the Hold expression with a replacement rule.
  • When leaving the block, list gets back its old definition, therefore all we have to do now is to remove the Hold so that the generated AppendTo expression is evaluated.


Posted 2012-03-06T21:39:16.347

Reputation: 18 543

Nice, and it can also be mapped over a list of variables! – István Zachar – 2012-03-06T22:36:43.253