1

I'm trying to write a function whose first argument must be another function, defined either as a pure function or a function-style replacement rule. I don't know how to specify a condition on the argument to achieve that. I know I can use g[func_Function,…] to require pure functions, but functions defined via replacement rules don't have a useful Head.

Here's an example:

f[x_] := x^2 - 1;
g[func_?MatchQ[#, _[___]] &, a_, b_] := func[a + b];
g[f, 1, 2]
(* g[f, 1, 2] *)

Checking the Head of f doesn't seem helpful:

Head[f] (* ==> Symbol *)
Head[f[x]] (* ==> Plus *)

How can I constrain the arguments to g such that it will accept either a pure function or a function defined with :=?

ibeatty
  • 2,533
  • 11
  • 22
  • 3
    There's no way to do this. I think you shouldn't. If you feel that you need a check, check that the result of applying the function is as expected. – Szabolcs Nov 07 '17 at 18:45
  • 4
    There are countless very different looking expressions in Mathematica which can all be "called" as if they were a function. By trying to restrict your input, you will inevitably prevent the use of at least some of these. – Szabolcs Nov 07 '17 at 18:46
  • 1
    See https://mathematica.stackexchange.com/questions/147942/wl-terminology-evaluate-call-invoke-use-etc – Alan Nov 07 '17 at 19:49
  • @Alan, I was very hopeful when saw System'Private'MightEvaluateWhenAppliedQ in the page you linked to, but I could't get it to do anything. Every time I tried using it, I just got my expression back unevaluated. :( – ibeatty Nov 08 '17 at 20:49
  • Try System`Private`MightEvaluateWhenAppliedQ[f], then f[x_] := x^2, then System`Private`MightEvaluateWhenAppliedQ[f] again. – Szabolcs Nov 10 '17 at 12:30
  • But even with this undocumented function, I maintain that trying to test if something is "a function" is not generally a good idea. There is never a guarantee that it will evaluate: if f[x_?NumberQ] := x^2 then neither of f[x] or f[Pi] or f[Sqrt[2]] will evaluate. If x /: g[x] = 1 then MightEvaluateWhenAppliedQ[g] is False but g[x] evaluates. While this is a contrived example, UpValues are really used like this in practice. This is how many of the group theory functions are implemented for groups not represented as permutation groups. – Szabolcs Nov 10 '17 at 13:03
  • @Szabolcs I hear you, and will keep it in mind for the future. But context is everything; for what I’m doing with my Mma neophyte students, handling f[___]:= and pure functions is adequate. – ibeatty Nov 11 '17 at 18:56
  • @ibeatty Ah, they're beginners and you mean you want to prevent them putting in x^2+1 where #^2 + 1& is needed. Viewed that way, it makes sense. – Szabolcs Nov 11 '17 at 18:59
  • @Szabolcs Yes, and because I’m trying to teach them the good habit of restricting input types. The current assignment requires writing functions that take a callback as an argument. – ibeatty Nov 11 '17 at 19:03

1 Answers1

1

You may Condition the function pattern such that it matches for a parameter of Head Function or on the DownValues of the symbol when they have the pattern of that matches a basic function define by SetDelayed.

As pointed out in the OP comments there are many forms that a function can take and the solution below will not cover them all. However, it may be enough to cover the cases within your particular project.

With

ClearAll[g]
g[func_, a_, b_] /; 
  MatchQ[func, _Function] \[Or] 
   Length@Cases[DownValues[func][[All, 1]], \[FormalP] : HoldPattern[_[_Pattern .., ___]] :> 
       Hold@\[FormalP], 2] > 0 :=
 func[a + b]

and

f[x_] := x^2 - 1

Then

g[f, 1, 2]
8
g[# + 1 &, 1, 2]
4
g[y, 1, 2]
g[y, 1, 2]

Hope this helps.

Edmund
  • 42,267
  • 3
  • 51
  • 143
  • This worked for me, but only after I removed the underscore after func in MatchQ[func_,_Function]. With the solution as written, I got an error about putting the pattern function on the right of a conditional. Also, you use foo_ in the argument list but func in the rest of the example; I presume that's just an oversight. – ibeatty Nov 08 '17 at 20:48
  • @ibeatty Sorry, hasty change of variables as I realised you used func in your question and I had used foo in my notebook. Fixed. – Edmund Nov 08 '17 at 21:12
  • 1
    @ibeatty Before you use this, consider things like InterpolatingFunction, CompiledFunction, the result of a LinearModelFit, functions defined with SubValues, e.g. add[x_][y_] := y+x, increment = add[1], and countless other examples. – Szabolcs Nov 10 '17 at 12:25