12
$\begingroup$

Perhaps this is poor programming on my part, but I would like to create a function that uses a variable as an optional parameter:

var = 2;
f[a_:var] := {a}

The desired output would be:

f[]
(* {2} *)
var = 5;
f[]
(* {5} *)

However the optional a maintains the value of var when f was defined:

var = 2;
f[a_:var] := {a}
var = 5;
f[]
(* {2} *)
f[5]
(* {5} *)

I can work around this somewhat by making the optional value dynamic:

var = 2;
f[a_:Dynamic[var]] := {a}
f[];
(* {2} *)
var = 5;
(* Above output updates to 5 *)

I can imagine having some fun with this type of behavior, but it is not what I want at the moment. Is it possible to set an optional parameter that can be updated? I'll also happily accept an answer that tells me why this would be a bad idea.

$\endgroup$
4
  • $\begingroup$ It is not what you are asking for but it does the same thing: f[x_]:={x}; f[]:={a}; $\endgroup$
    – Kuba
    Commented Jul 22, 2013 at 20:44
  • 2
    $\begingroup$ All you have to do is to define your function before var has any value, or use Block, such as Block[{var}, f[a_:var]:={a}]. See also this answer and a discussion in comments below it. $\endgroup$ Commented Jul 22, 2013 at 20:46
  • 2
    $\begingroup$ Related FYI: Default appears to allow variable values, but it does not. You may already know this, but it's tripped up quite a few users I believe. Also, I see that Simon (I miss his participation) posted effectively the same method that Leonid just showed in answer to that old question of mine. $\endgroup$
    – Mr.Wizard
    Commented Jul 23, 2013 at 8:54
  • $\begingroup$ Closely related, possible duplicate: (26354) $\endgroup$
    – Mr.Wizard
    Commented Mar 8, 2020 at 3:33

3 Answers 3

13
$\begingroup$

This is possible to do, in at least two ways. The first method is to make sure that your function's definition is entered first, so that the default variable var has no value yet:

ClearAll[var,f];
f[a_: var] := {a};
var = 2;

f[]

(* {2}  *)

var = 5;
f[]

(* {5}  *)

The second method would work regardless of whether or not the variable var has currently a value:

ClearAll[var, f];
var = 2;
Block[{var}, f[a_: var] := {a}]

and then again:

f[]

(* {2}  *)

var = 5;
f[]

(* {5}  *)

The reason this works is that what matters is only what var evaluates to at the moment of definition. It is then being evaluated by SetDelayed, and its value is what is being used to form the resulting global rule. Once that global rule (definition) has been added to the global rule base, it does not matter any more what happens to var after that.

A closely related discussion can be found here. A question of whether or not to actually use such constructs is a matter of taste. I don't see a good reason to not recommend ever using it. If you use the Block-based method, then it should be reasonably safe. Usual warnings related to the use of global variables do apply, but I do acknowledge that there may be cases where such construct may be justified.

$\endgroup$
11
  • $\begingroup$ Thanks - your discussion makes perfect sense, and makes me think I should have figured this one out myself! $\endgroup$ Commented Jul 22, 2013 at 21:02
  • 1
    $\begingroup$ @bobthechemist Glad I could help, and thanks for the accept. I think this was the fastest accept for any of my answers - within 3 minutes of posting the answer :). You could have waited for some time with accepting though, to encourage others to contribute more answers. As for the subject matter, this actually seems to be one of these issues which look very simple once we know the answer, but actually are not so simple. I seem to remember that it took me some time to figure this out when I asked myself the same question, back when I didn't know the answer. $\endgroup$ Commented Jul 22, 2013 at 21:04
  • $\begingroup$ I was been playing with and being frustrated by this just last week. You're right, obvious, once you know the answer. $\endgroup$
    – rcollyer
    Commented Jul 22, 2013 at 21:18
  • $\begingroup$ @rcollyer Well, sorry to hear that. The answer was also on SO, but my version was only in comments to Brett's answer, and one has to remember which question was that. In fact, since it was in comments, it took me some time to find that discussion now. I only managed to do find it because I remembered who posted one of the answers to that question (Brett). $\endgroup$ Commented Jul 22, 2013 at 21:20
  • $\begingroup$ @LeonidShifrin I do typically try to hold off accepting answers so that others may comment, but yours hit the nail right on the head so I figured I would accept and move on. $\endgroup$ Commented Jul 22, 2013 at 21:40
6
$\begingroup$

Another possibility is to use HoldPattern:

var = 2;
HoldPattern[f[a_: var]] := {a};

Then:

f[]
(* 2 *)

And then:

var = 5;
f[]
(* 5 *)
$\endgroup$
3
$\begingroup$

Just for completeness, here is another way:

var = 2;
DownValues[f] = HoldPattern[f[a_ : var]] :> {a};

Now

f[]
{2}

and

var = 5;
f[]
{5}
$\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.