20
$\begingroup$

I would like to know the name of the current function from within that function. For example, consider the following code

myFunction[args___] :=
  Module[{},
    checkArgs[args,"myFunction"];
    someCode[]
  ];

where checkArgs is used in several functions which have the same types of arguments and reports an error message which includes the name of the function it was called from. I would like to call checkArgs without having to give it the name of the function.

The evaluation stack, obtained using Stack, I believe does not contain this information unless called from within a StackComplete, and this is generally not the case in normal use of the function.

I appreciate that "the current function" is not necessarily a well-defined concept in Mathematica, since you can have nested function definitions, and a function is just a rule definition anyway. However, the options management system uses this concept. If I have OptionsPattern in the function definition, I can call OptionValue, and something knows to look in Options[myFunction] to find the information about the options. So could whatever this mechanism is using be accessed? Is there a way to get the current function from the options system?

The best solution I have come up with is this:

(fn:myFunction)[args___] :=
  Module[{},
    checkArgs[args,fn];
    someCode[]
  ];

which avoids having to pass in the function name explicitly, but is still quite ugly.

$\endgroup$
  • $\begingroup$ Apart from being ugly, what shortcomings does your workaround solution have? I'm not sure that Leonid's answer is not an overkill if the only real difference is uglyness. Nevertheless, would be nice to have a built-in solution a la OptionValue. $\endgroup$ – István Zachar Aug 3 '16 at 20:41
  • 1
    $\begingroup$ A related thread. $\endgroup$ – J. M. will be back soon Aug 7 '16 at 20:53
15
$\begingroup$

Custom assignement operators

Not quite what you asked for, but (as we already discussed recently), you can use custom assignment operators to define some variable that would have the value set to the name of your function inside its body. Here is a possibility:

ClearAll[def];
SetAttributes[def, HoldAll];
def /: SetDelayed[def[f_[args___]], rhs_] :=
   f[args] := Block[{$inFunction = f}, rhs];
Protect[def];

The above assignment operator is based on dynamic scoping. In case you want to bind lexically, you can do a similar thing, which in this case becomes a macro:

ClearAll[deflex];
SetAttributes[deflex, HoldAll];
deflex /: SetDelayed[deflex[f_[args___]], rhs_] :=
   Hold[rhs] /. HoldPattern[currentFunction] :> 
       f /. Hold[code_] :> SetDelayed @@ Hold[f[args], code];
Protect[deflex]

In contrast to def, the deflex operator does not wrap Block[{$inFunction = f}, ...] around the body of your function, but rather replaces all literal occurrences of currentFunction in the body of your function with f, before making a definition.

Example

The above form allows for a natural-looking code:

def @ myFunction[args___] :=
   Module[{}, checkArgs[args, $inFunction]]

so that you just have to add def @ to the usual definition. Here is the generated defintion:

?myFunction
Global`myFunction
myFunction[args___]:=
    Block[{$inFunction=myFunction},Module[{},checkArgs[args,$inFunction]]]

And here is a test:

myFunction[1,2,3]

(* checkArgs[1,2,3,myFunction] *)

You can do a similar thing with deflex:

ClearAll[mySecondFunction];
deflex @ mySecondFunction[args___] :=
   Module[{}, checkArgs[args, currentFunction]]

although the generated definition will be different:

?mySecondFunction
Global`mySecondFunction
mySecondFunction[args___]:=Module[{},checkArgs[args,mySecondFunction]]

In this case, both definitions efectively result in a similar run-time behavior, but there can be situations where this will not be the case.

Limitations

Note that this form of def (or deflex) can not handle definitions of this type:

def @ myFunction[args___] /; Length[{args}] > 1 :=
   Module[{}, checkArgs[args, $inFunction]]

but can handle a similar one like this:

def @ myFunction[args___] :=
   Module[{}, checkArgs[args, $inFunction]] /; Length[{args}] > 1
$\endgroup$
  • 2
    $\begingroup$ A bit OT, but I always wondered: How does the magic behind OptionValue work? $\endgroup$ – sebhofer Mar 4 '13 at 17:11
  • 1
    $\begingroup$ @sebhofer The best I can tell at the moment is what is described in my answer to this question. $\endgroup$ – Leonid Shifrin Mar 4 '13 at 17:25
  • $\begingroup$ Both solutions look amazing! IMO the later produces a more concise result; the former solution is brilliant in conception, the later one I really am not sure how it works; pure magic as far as I'm concerned. I'm really happy I saw these; they made my day $\endgroup$ – yosimitsu kodanuri Apr 4 at 5:41
  • $\begingroup$ @yosimitsukodanuri Well, they are similar, except the latter one processes the code of the body of the function, adding the function name at definition time, while the former wraps the body in Block, making a special variable available at run-time. In some cases, either one can be used, in others one might be preferable. In any case, glad you find them useful. $\endgroup$ – Leonid Shifrin Apr 4 at 5:44
  • $\begingroup$ @Leonid Shifrin I get how the Block one works; I hope I'll understand the RuleDelayed within the month; brilliant! thank you $\endgroup$ – yosimitsu kodanuri Apr 4 at 6:33
10
$\begingroup$

If a current evaluation rule contains/uses OptionsPattern[] then you can abuse the fact that OptionName[a] is replaced by OptionName[functionName, {}, a].

foo[OptionsPattern[]] := Hold[OptionValue[whatever]][[1, 1]]

(*DownValue: HoldPattern[foo[OptionsPattern[]]] :> Hold[OptionValue[whatever]][[1, 1]] *)

foo[]

foo

$\endgroup$
  • 2
    $\begingroup$ Oh, that's a neat trick! Thanks for sharing. $\endgroup$ – Sjoerd Smit Apr 2 at 21:19
  • $\begingroup$ @SjoerdSmit yep, it is interesting $\endgroup$ – Kuba Apr 13 at 7:11
5
$\begingroup$

Well, you can exploit OptionValue for this purpose, but of course this is not the answer you are looking for:

ClearAll[myFunction, checkArgs, someCode];
Options[myFunction] = {"Name" -> myFunction};
myFunction[arg_, OptionsPattern[]] := Module[{},
   checkArgs[arg, OptionValue@"Name"];
   someCode[];
   ];

Downside is that you have to maintain options. Also, you cannot really prevent something passing on a fake "Name"->otherFunction argument to your function. If all your functions have a "Name" option then at least you don't have to make the checkArgs call function-specific.

Of course you can use "$$ObscureOptionUserShouldNotMeddleWith643x84" instead of "Name".

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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