Passing a function as an argument to another function using the same variables

I define a function with two variables and an argumment which is a function of this two first variables. For example the function Ttrho below (the two variables are T and Rho and pTt is a function of T or Rho or both):

getAllVariables[expr_] :=
Union@Cases[expr,
Except[__Symbol?(Context@# ===
"System" &), _Symbol], {1, ∞}, Heads -> True];

Ttrho[T_, ρ_, pTt_] :=
With[{var = getAllVariables[pTt]}, Print[var];
If[Length@var > 0,
Apply[pTt, var /. Thread[var -> {T, ρ}]] 5 0.069/ρ,
pTt*5 0.069/ρ]]


I would like that the three results below must have the same value:

Ttrho[1, 0.1, Function[{T, ρ}, T]]
Ttrho[1, 0.1, Function[{ρ, T}, T]]
Ttrho[1, 0.1, Function[T, T]]


But I obtain actually the following results:

During evaluation of In[1]:= {T,ρ}

Out[1]= 3.45

During evaluation of In[2]:= {T,ρ}

Out[2]= 0.345

During evaluation of In[3]:= {T}

During evaluation of In[3]:= Thread::tdlen: Objects of unequal length in {T}->{1,0.1} cannot be combined.

Out[3]= 3.45

I am struggling to understand the motivation behind this question and therefore the actual behavior that you are seeking. However it seems to me that perhaps you want something like this.

ClearAll[Ttrho]

Ttrho[xT_, xρ_, HoldPattern@Function[_, body_]] :=
Block[{T = xT, ρ = xρ}, body]

Ttrho[1, 0.1, Function[{T, ρ}, 3 T]]
Ttrho[1, 0.1, Function[{ρ, T}, 3 T]]
Ttrho[1, 0.1, Function[T, 3 T]]

3

3

3


This will only work with actual Function expressions as shown, not generic functions of any kind, but since you appear to want commonality of parameter names I think this is implied.

Application

To make this "elegant" please load my blockSet function from (69595) then:

ClearAll[Ttrho]

blockSet[
Ttrho[T_, ρ_, pTt_] := pTt[[2]]*5 0.069/ρ
]

Ttrho[1, 0.1, Function[{T, ρ}, T]]
Ttrho[1, 0.1, Function[{ρ, T}, T]]
Ttrho[1, 0.1, Function[T, T]]

3.45

3.45

3.45


This also relies on the third argument of Ttrho being an explicit Function as pTt[[2]] is used to extract its body.

• The motivation is that Ttrho is a function which represents a property of fluids. This property has a general shape for all fluids but some coefficients like pTt are different functions (it can also be a constant) for different fluids. These functions (or constants) are given in a dataset. So T and rho represents always the same physical quantity. So when the parameter ptT is simply a constant (rather than a function) it is simpler for the user to put the value instead of writing a function? Commented Feb 3, 2017 at 16:43
• Does with the blockset function, ptT can be any kind of function? Commented Feb 3, 2017 at 16:56
• @F.Aitken I just read your comments, and pardon me, but I'm not sure I understand this any better. The issue I see is that you have a function Function[{T, ρ}, T] and a function Function[{ρ, T}, T] which have the parameters in different order; the only way to tell which parameter is T and which is ρ is to "look" at those Symbols themselves. If we had another function Function[{x, y}, 2 x + y] what should we (and our Ttrho) make of it? Likewise 2 # + #2 & or any other generic function that does not have explicit parameters T and/or ρ. Commented Feb 3, 2017 at 17:43

You may use Symbol and the Parts of Expressions tutorial.

With

tRho[t_, rho_, f_Function] :=
(f[[2]] /. {Symbol["t"] -> t, Symbol["rho"] -> rho}) 5 0.069/rho


Then

tRho[1, 0.1, Function[{t, rho}, t]]
tRho[1, 0.1, Function[{rho, t}, t]]
tRho[1, 0.1, Function[t, t]]

3.45
3.45
3.45


Hope this helps.

• To complete this you still need to localize t and rho. Presently these symbols evaluate as a result of both f[[2]] and Symbol["t"] etc. Commented Feb 7, 2017 at 18:31
• @Mr.Wizard I'm not certain I am following what you are saying. If I evaluate t = 10*10^9 and then evaluate tRho[1, 0.1, Function[t, t]] then I still get 3.45. Why do I need to localise with Module or Block? Commented Feb 7, 2017 at 20:39
• Your example works but for the wrong reason. :^) You end up with something like 10000000000 /. 10000000000 -> new but one should never let it get to that point. Consider t := RandomReal[] instead; now the function is broken! Our code should be robust against such things. Commented Feb 7, 2017 at 22:03
• @Mr.Wizard So true. Trace reveals all. That would have been one nasty bug to locate. Thanks. Commented Feb 7, 2017 at 23:59

In Function[{T, ρ}, body] expression T and ρ are just "dummy variables", their names are not really meaningful, only meaningful thing is relation between T and ρ symbols, used inside function body, to positions of those symbols in arguments list. Mathematica can rename function variables, to avoid conflicts in nested scopes. So even if T or ρ was typed inside Function, resulting expression can contain different symbols. That's why extraction of function body and identification of variables, that you asked for in question, can be tricky.

I would take advantage of Mathematica symbolic capabilities and, instead of Functions, I'd use expressions containing globally meaningful symbols. So I'd put T and ρ in some context, that I'd keep on $ContextPath, and, instead of Function[{T, ρ}, T], just pass T to your Ttrho function. I would also separate concerns of calculating Ttrho expression and of inserting numerical values for T and ρ. Putting it together I'd use something like this: BeginPackage@"myThermodynamics"; Unprotect@"*"; ClearAll@"*" T::usage = "T is something"; ρ::usage = "ρ is something else"; Ttrho::usage = "Ttrho[pTt] returns something"; Begin@"Private"; ClearAll@"*" someConstant = 5 0.069; Ttrho@pTt_ := someConstant pTt/ρ End[]; Protect@"*"; EndPackage[];  So now you can calculate symbolic expression for Ttrho and insert numeric values like this: Ttrho[T] % /. {T -> 1, ρ -> 0.1} (* (0.345 T)/ρ *) (* 3.45 *) Ttrho[ρ^3/T^2] % /. {T -> 1, ρ -> 0.1} (* (0.345 ρ^2)/T^2*) (* 0.0345 *)  If you really need to extract body from already existing Function expression you could use something like following: funcToExpr // ClearAll funcToExpr@func:HoldPattern@Function[ Except[Null, var_Symbol] | {vars___Symbol}, Repeated[_, 2] ] := func @@ Flatten@Replace[HoldComplete[vars, var], s_ :> With[{name = SymbolName@Unevaluated@s}, RuleCondition[ ToExpression[Context@Unevaluated@s <> StringDrop[name, -1], InputForm, HoldComplete ], StringEndsQ[name, "$"]
]
],
{1}
]


Which should work also for Functions with renamed variables:

Function[T, T]
% // funcToExpr
(* Function[T, T] *)
(* T *)

With[{x = 2}, Function[T, x T]]
% // funcToExpr
(* Function[T$, 2 T$] *)
(* 2 T *)

Function[{T, ρ}, T ρ]
% // funcToExpr
(* Function[{T, ρ}, T ρ] *)
(* T ρ *)

With[{x = 2}, Function[{T, ρ}, x T ρ]]
% // funcToExpr
(* Function[{T$, ρ$}, 2 T$ρ$] *)
(* 2 T ρ *)


Putting all things together:

Ttrho@funcToExpr@#3 /. {T -> #1, ρ -> #2} & @@@ {
{1, 0.1, Function[{T, ρ}, T]},
{1, 0.1, Function[{ρ, T}, T]},
{1, 0.1, Function[T, T]}
}
(* {3.45, 3.45, 3.45} *)


Thanck you very much for the different propositions. By using the blockSet function of Mr Wizard with the two additional conditions:

ClearAll[Ttrho]

ConstanteQ[f_] :=
NumberQ[f] ==
True || (QuantityQ[f] == True && (Head@f[[1]] === Function) ==
False);
QuantityFunctionQ[f_] :=
QuantityQ[f] == True && Head@f[[1]] === Function;

blockSet[
Ttrho[T_, \[Rho]_, pTt_: 1] :=
With[{fpTt =
If[ConstanteQ[pTt], pTt,
If[QuantityFunctionQ[pTt], Quantity[Last@pTt[[1]], pTt[[2]]],
pTt[[2]]]]},
fpTt*5 0.069/\[Rho]]
]


I obtained a great flexibility on the way in which the user can enter the function (or the constant) ptT. For the different examples below:

Ttrho[Quantity[2, "Kelvins"], 0.1, Function[{T, \[Rho]}, 2 T]]
Ttrho[Quantity[2, "Kelvins"], 0.1, Function[{\[Rho], T}, 2 T]]
Ttrho[Quantity[2, "Kelvins"], 0.1, Function[T, 2 T]]

Ttrho[2, 0.1, Quantity[Function[T, 2 T], "Kelvins"]]
Ttrho[2, 0.1, Quantity[Function[{T, \[Rho]}, 2 T], "Kelvins"]]
Ttrho[2, 0.1, Quantity[Function[{\[Rho], T}, 2 T], "Kelvins"]]
Ttrho[2, 0.1, Function[T, Quantity[2 T, "Kelvins"]]]
Ttrho[2, 0.1, Function[{T, \[Rho]}, 2 T]]

Ttrho[Quantity[2, "Kelvins"], 0.1, Function[T, 2]]
Ttrho[Quantity[2, "Kelvins"], 0.1, 2]
Ttrho[Quantity[2, "Kelvins"], 0.1, Function[T, Quantity[2, "Kelvins"]]]
Ttrho[Quantity[2, "Kelvins"], 0.1, Quantity[2, "Kelvins"]]

Ttrho[2, 0.1, Quantity[Function[\[Rho], 2 \[Rho]^2], "g/cm^3"]]
Ttrho[2, 0.1, Quantity[Function[{\[Rho]}, 2 \[Rho]], "g/cm^3"]]


You get the following results:

Quantity[13.8, "Kelvins"]

Quantity[13.8, "Kelvins"]

Quantity[13.8, "Kelvins"]

Quantity[13.8, "Kelvins"]

Quantity[13.8, "Kelvins"]

Quantity[13.8, "Kelvins"]

Quantity[13.8, "Kelvins"]

13.8

6.9

6.9

Quantity[6.9, "Kelvins"]

Quantity[6.9, "Kelvins"]

Quantity[0.069, ("Grams")/("Centimeters")^3]

Quantity[0.69, ("Grams")/("Centimeters")^3]

Sorry I have not yet looked at the solution proposed by jkuczm and perhaps it has a greater flexibility than above?

• It would be better, if you combined this with your original question. Commented Feb 11, 2017 at 3:48