Specifying optional arguments with variables

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.


Another possibility is to use HoldPattern:

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

Then:

f[]
(* 2 *)

And then:

var = 5;
f[]
(* 5 *)

Just for completeness, here is another way:

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

Now

f[]
{2}

and

var = 5;
f[]
{5}