1
$\begingroup$

One can write functions which depend on the type of actual parameter before they are actually called. E.g.:

Clear[f,g,DsQ];
DsQ[x_]:=MatchQ[x,{String__}];
f[i_Integer, ds_?DsQ] :=Print["called with integer i and DsQ[ds]==True"];
f[i_String, ds_?DsQ]  :=Print["called with String i and DsQ[ds]==True"];
f[i_?NumberQ, ds_?DsQ]:=Print["called with numerical parameter"];
f[i_?NumberQ]:=Print["only a numerical parameter"];

(* Tests *)
ds={"string"};
DsQ[ds]
f[1,ds]
f[1.,{""}]
f[999]

yields

True
called with integer i and DsQ[ds]==True
called with numerical parameter
only a numerical parameter

Now I want to write another function g, which gets a function like f as parameter. But the functional parameter’s definition’s left hand side shall have a defined form, e.g. only the one from the first definition of f

f[i_Integer, ds_?DsQ]

Is such a check for the right pattern of a functional parameter possible? How do I do that? Writing

g[p_Symbol[i_Integer, ds_?DsQ]]:=Print["g called with first type of definition"]

does not work.

$\endgroup$
9
  • 1
    $\begingroup$ It's not quite clear to me what you want to achieve: How do you want to call g? As g[f]? What should happen if f has multiple different definitions (like in your example)? What do you ultimately want to use this for? $\endgroup$
    – Lukas Lang
    Dec 14, 2022 at 22:39
  • 1
    $\begingroup$ I think what you want is to set attributes of g so that it holds its argument. Something like SetAttributes[g, HoldAll]. Try it out and see if it works: g[f[1, {""}]] prints "g called with first type of definition" but g[f[0.1, {""}]] returns unevaluated. If that's what you want, I can write a quick answer. $\endgroup$
    – march
    Dec 14, 2022 at 23:09
  • $\begingroup$ I agree with @LukasLang that this looks like an XY problem. If you could describe in detail what you are trying to achieve (at high level), then you’ll likely get better answers. $\endgroup$
    – Roman
    Dec 15, 2022 at 13:47
  • $\begingroup$ Since you tried g[p_Symbol[i_Integer, ds_?DsQ]] instead of g[f[i_Integer, ds_?DsQ]], I'm inferring that what you mean by "type" is something like signature. Is that correct? I.e. the function g doesn't actually know about the function f explicitly, but will do something for any p that has a particular signature. If this is on the right track, it would be very helpful to have more context. What do you need g to do with p and with the arg pattern? $\endgroup$
    – lericr
    Dec 15, 2022 at 15:30
  • $\begingroup$ I ask, because as it stand, I don't think you can come up with something generally resilient. For example, one can't expect f[i_Integer, ds_?DsQ] to be a sort of canonical form for that signature. Indeed, something like f[i_Integer, ds : {__String}] might be more expected. $\endgroup$
    – lericr
    Dec 15, 2022 at 15:34

3 Answers 3

0
$\begingroup$

The call patterns are stored under the name of "DownValues":

DownValues[f]

enter image description here

Therefore, the part that determines the type can be extracted by:

DownValues[f][[1, 1, 1, 2]]

enter image description here

and you can define g e.g. like:

g[i_ : Integer, DownValues[f][[1, 1, 1, 2]] ] := 
 Print["g called with integer and string-list"]

g[1, {"y"}]
(* g called with integer and string-list *)
$\endgroup$
2
  • $\begingroup$ Daniel, somehow you mistyped something which prevents me from seeing the clue which was about to follow your last colon. $\endgroup$ Dec 15, 2022 at 16:38
  • $\begingroup$ Sorry, I added the missing piece. $\endgroup$ Dec 15, 2022 at 17:52
0
$\begingroup$

Here's a suggestion that differs from your approach but may be useful. For every function (here, f and h) we define a "correct" interface and a "catchall" interface that is used whenever the function is called incorrectly. The "catchall" interface (with the pattern x___) aborts the running calculation.

Clear[f, g, h, DsQ];
DsQ[x_] := MatchQ[x, {String__}];

f[i_Integer, ds_?DsQ] := Print["f called with integer i and DsQ[ds]==True"];
f[x___] := Module[{},
             Print["f called with different arguments: ", {x}];
             Abort[]]

h[i_Integer] := Print["h called with integer i"];
h[x___] := Module[{},
             Print["h called with different arguments: ", {x}];
             Abort[]]

g[p_Symbol] := p[7, {"a"}] + p[8, {"b"}] + p[9, {"c"}]

We can use f to call the function g:

g[f]
(*    f called with integer i and DsQ[ds]==True
      f called with integer i and DsQ[ds]==True
      f called with integer i and DsQ[ds]==True
      3 Null                                       *)

But we're not allowed to use h to call the function g:

g[h]
(*    called with different arguments: {7,{a}}
      $Aborted                                     *)

Of course your action in the "catchall" may be different from Abort[].

$\endgroup$
0
$\begingroup$

These examples show better which expression causes a specific print line than in my original post:

ClearAll[f,g,DsQ];
DsQ[x_]:=MatchQ[x,{String__}];

f[x___] := (Print["f: called with illegal arguments f[", x, "]"]; Abort[]);   (* added catch all, tx Roman *)
f[i_Integer, ds_?DsQ] := (Print["f: called with integer ", i, 
" and DsQ[ds]==True"]; "Result: "<>ToString[i]<>", "<>ToString[ds]);
f[s_String, ds_?DsQ]  := (Print["f: called with String ", s, " and DsQ[ds]==True"]; "Result: "<>s<>", "<>ToString[ds]);

(* Tests *)
ds={"string ds"};
DsQ[ds]
f[1, ds]
f["hello", ds]

yields

True

f: called with illegal arguments f[forbidden!]
$Aborted

f: called with integer 1 and DsQ[ds]==True
Result: 1,{string ds}

f: called with String hello and DsQ[ds]==True
Result: hello, {string ds}

The other function g gets a function like the first form of f as parameter. The functional parameter’s definition’s left hand side shall have a defined form, e.g. only the one from the first definition of f

f[i_Integer, ds_?DsQ]

and not e.g. the other way around like

h1[ds_?DsQ, i_Integer]

or

h2[i_Integer, j_Integer]

or any form of f but the first one.

This is the appropriate pattern for the functional parameter on the LHS:

g[p_Symbol[i_Integer, ds_?DsQ]]:=Print["g: called with first type of definition, f returns ", f[i, ds]];

In this one

g[f[3, {"three"}]] 

f is evaluated first and returns a string. We see the Print from f and the unevaluated expression of g with the result from f which does not match g's definition pattern, therefore we get:

g[Result: {three}]]

But after setting HoldAll it works:

SetAttributes[g, HoldAll]
g[f[4, {"four"}]]

It prints

f: called with integer 4 and DsQ[ds]==True
g: called with first type of definition, f returns Result: 4, {four}

which is what I was after.

$\endgroup$

Your Answer

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

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