6
$\begingroup$

Background

Suppose I have begun a fresh kernel session, and I have defined the following function alone:

F[x_, y_] := x + y;

One expected use of F is this:

F[0, 1]

Consider three examples of unexpected uses. One with the wrong name, one with an incorrect number of arguments, and a final one with an expected application of + to an unexpected application of F and 1.

G[0, 1]
F[0]
F[0] + 1

All of these are valid Mathematica expressions, however I am not interested in them in this context.

Question

How can I ensure that an expressions contains no unevaluated function applications?

  1. Is there a function which takes an expression and asserts this requirement?
  2. Is there a function which asserts this over all following expressions in the context?
$\endgroup$
2

2 Answers 2

7
$\begingroup$

The following function checks whether an expression contains anything of the form f_[___] (i.e. an unevaluated function call), where f is a symbol in the current context1 (to ensure that e.g. Sin[3] is allowed):

functioncall::unev = "Unevaluated function calls!";
check = If[
    ! FreeQ[#, x_Symbol[___] /; Context@x == $Context, All],
    Message[functioncall::unev];
    #,
    #
    ] &;

F[x_, y_] := x + y;

check[F[1, 0]]
(* 1 *)

check[F[x, y]]
(* x + y *)

check[G[x, y]]
(* functioncall::unev: Unevaluated function calls! *)
(* G[x, y] *)

check[F[0]]
(* functioncall::unev: Unevaluated function calls! *)
(* F[0] *)

If you want to apply this function to every output, you can assign the function check to $Post:

$Post = check;

1 The terms "context" and "delayed evaluation" have different meanings in the context of Mathematica than what you seem to think: What you call "context", I would call "(kernel) session" (see $Context for what context means). What you call "delayed evaluation", I would call "inert expression" (delayed evaluation is used more in the context of evaluating the r.h.s of SetDelayed, TagSetDelayed and RuleDelayed)

$\endgroup$
4
  • $\begingroup$ Thanks. That seems to do the job. It is a shame that this isn't provided as a language construct. Just one question: isn't the All argument implicit in FreeQ? $\endgroup$
    – justinpc
    Jan 24, 2020 at 13:52
  • $\begingroup$ @justinpc I don't think it would really go with the "spirit" of Mathematica. The big advantage is you can use symbolic expressions in the same way as actual numbers. (If you simply want to ensure that you get a number, see NumericQ) $\endgroup$
    – Lukas Lang
    Jan 24, 2020 at 13:56
  • $\begingroup$ I will also edit my question a bit to coincide with the context of your answer. Another question: "inert" does not seem to be a term in the Mathematica documentation. Do you have a more contextual term for this? Right now I am leaning towards "unevaluated function application". $\endgroup$
    – justinpc
    Jan 24, 2020 at 13:56
  • $\begingroup$ Yes, I had the same feeling regarding the spirit issue. It's a tough question. Mathematica provides facilities both for symbolic manipulation, but it also has "Programmatic Notebooks" and facilities for multiple different scoping paradigms. What we are talking about here are in fact multiple different evaluation paradigms. $\endgroup$
    – justinpc
    Jan 24, 2020 at 13:59
4
$\begingroup$

You could define

symbol[___]:=$Failed

For every existing symbol from Names["`*"] or for all that will be created by using $NewSymbol.

But I would not do that globally. There are many use cases for custom expressions that are 'unevaluated' see for example: 11436

What I do is to associate symbol[___]:=$Failed when I define a function. I do this so often that I have a function for this: MFailByDefault

The definition is at the bottom or you can also install Meh`, its parent package: https://github.com/kubaPod/Meh

(*optional package setup*)
ResourceFunction["GitHubInstall"]["kubapod","meh"];
Needs @ "Meh`";

foo // ClearAll;
foo[x_, y_] := x + y

foo // MFailByDefault

foo[1, "X", PlotRange -> Automatic]

foo::argpatt: There are no rules associated with signature foo[Integer, String, Rule].

enter image description here

It issues a message because I don't like the silent way functions fail. It also returns a Failure because I prefer checking for failures rather than catching messages.

Quick definition

MFailByDefault[symbol_Symbol]:= (
  symbol::argpatt = Meh::argpatt
  ; symbol[x___]:= MGenerateAll[symbol::argpatt, inputToSignature[symbol[x]]]
);

inputToSignature // Attributes = {HoldAllComplete};

inputToSignature[head_[spec___]]:= ToString[#, OutputForm]& @ StringForm[
  "``[``]"
, head
, Row[ Thread @ HoldForm[{spec}][[{1}, ;;, 0]], ", "]
];
$\endgroup$
3
  • $\begingroup$ Thanks for your answer. Would you include the definition of MGenerateAll? $\endgroup$
    – justinpc
    Jan 24, 2020 at 14:35
  • $\begingroup$ Also could you explain what you mean by "checking for failures"? Do you mean that they visually stand out more, or is there some sort of functionality for checking for failures? $\endgroup$
    – justinpc
    Jan 24, 2020 at 14:36
  • 1
    $\begingroup$ @justinpc ah, right, will fix it on Monday. I meant I can do FailureQ, you can pass additional information inside etc. It comes very handy in control flow aspects. For purely interactive coding/research it may be less useful though. $\endgroup$
    – Kuba
    Jan 25, 2020 at 9:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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