# How to specify optional arguments that take functional values

I am trying to make a function with an optional argument that may change but mathematica caches the value when the function is defined.

Here is an example where the optional time variable is set when the function is defined.

Remove[CurrentTime]
CurrentTime[time_: DateList[]] := {time, DateList[]};
Pause[1]
CurrentTime[]


The output is split by 1 second

{{2013, 6, 4, 13, 57, 46.149504}, {2013, 6, 4, 13, 57, 47.150726}}


Is there a way to tell it its a delayed set, or to just read the function each time?

• ...what sort of behavior were you expecting? Jun 4 '13 at 4:34
• I want the time argument to default to the current time in this case, ie, they should show the same time. Jun 4 '13 at 4:40
• Well, the problem is that you're effectively evaluating {DateList[], DateList[]}; it stands to reason that some amount of time should have to pass in between the two times you call DateList[]... Jun 4 '13 at 4:45
• That is by design to illustrate the problem, there is a pause of 1 second to show that the default value for time is set once and never changed. I would like to make a function in which you can pass in the time, but if you dont it defaults to the current time. Jun 4 '13 at 4:49
• Well, you might be interested in CurrentTime[OptionsPattern[{"Time" :> DateList[]}]] := {OptionValue["Time"], DateList[]}. One could either do CurrentTime[] or CurrentTime["Time" -> {2013, 6, 4, 12, 0, 0}]... Jun 4 '13 at 4:52

CurrentTime[time_: Hold[DateList[]]] := {ReleaseHold@time, DateList[]};
Pause[1]
CurrentTime[]


{{2013, 6, 4, 8, 29, 34.8437500}, {2013, 6, 4, 8, 29, 34.8437500}}

Edit

This works too:

Remove[CurrentTime]
SetAttributes[CurrentTime, HoldAll]
CurrentTime[time_: DateList[]] := {time, DateList[]};

• Ah, it was the Attributes[]... I do think your second solution is much better than the first. Jun 4 '13 at 6:42
• @0x4A4D It is more elegant but if, for example, You have 3 arguments with Hold and 4 without and only HoldAll, HoldRest, HoldFirst Attributes avalible then the first is useful.
– Kuba
Jun 4 '13 at 6:49

HoldPattern and Unevaluated work too. You can also remove the HoldAll once you made the definition if you like.

ClearAll[f, g, h]
f[HoldPattern@Optional[x_, RandomReal[]]] := x
g[Unevaluated@Optional[x_, RandomReal[]]] := x
h~SetAttributes~HoldAll
h[Optional[x_, RandomReal[]]] := x
h~SetAttributes~{}
h~ClearAttributes~HoldAll
{f[], f[]}
f[1]
{g[], g[]}
g[1]
{h[], h[]}
h[1]

{0.683758, 0.584808}

1

{0.793986, 0.0783635}

1

{0.518827, 0.806583}

1


You just need to get the function you want to give the default argument unevaluated into the replacement rule that is created: DownValues@g now gives

{HoldPattern[g[x_ : RandomReal[]]] :> x}

You could even do

Unprotect@Optional
SetAttributes[Optional, HoldRest]
ll[Optional[x_, RandomReal[]]] := x
ll[]
ll[1]

• @Kuba Unfortunately, that does not work: Calling g[] then gives Unevaluated[RandomReal[]] Aug 18 '16 at 16:23
• Agreed, I've realised this today morning but forgot to delete the comment :)
– Kuba
Aug 18 '16 at 16:25
HeldOptional = Function[, HoldPattern@Optional[#1, #2], HoldRest]

Remove[CurrentTime]
CurrentTime[time_~HeldOptional~DateList[]] := {time, DateList[]};
Pause[1]
CurrentTime[]

• Sure wish they'd put this in the Help. As of 11.2 there is no mention of such: ref/Optional. Jun 13 '19 at 13:04

I would use Automatic as the default value, then do this:

currentTime[time_: Automatic] := Replace[time, Automatic -> DateList[]]


I used Replace, not ReplaceAll. Unlike ReplaceAll, Replace is safe because it only does the replacement when the whole expression matches. It won't replace subexpressions.