The idea
If you adhere to the convention that the size argument is always first in all your functions, then what you ask for can be achieved with some metaprogramming / dynamic environments. A dynamic environment is a function that takes your code, and generally modifies some definitions locally, only for the code that runs inside it.
Preparation
As a simple example, consider these 3 functions:
diag[size_, diagElement_] := DiagonalMatrix[ConstantArray[diagElement, size]]
identity[size_] := IdentityMatrix[size];
norms[size_, genF_, dotF_] := Outer[dotF, #, #] &[genF /@ Range[size]]
They take the matrix size and sometimes other parameters, and generate various matrices. For example:
diag[2, 10]
(* {{10, 0}, {0, 10}} *)
identity[2]
(* {{1, 0}, {0, 1}} *)
norms[
2,
Function[x, HermiteH[#, x]] &,
Function[{l, r}, Integrate[l[y]*r[y]* Exp[-y^2], {y, -Infinity, Infinity}]]
]
(* {{2 Sqrt[\[Pi]], 0}, {0, 8 Sqrt[\[Pi]]}} *)
where the last matrix is made of dot products of Hermite polynomials.
Automating redefinitions
What I suggest to do is to create a dynamic environment in which all these functions will be redefined to automatically assume the first (in this case) argument size to some fixed value.
Here is the code:
redefine[syms:{___Symbol}, f_]:=
Module[{inSym},
Scan[
Function[sym,
With[{protected = Unprotect[sym]},
sym[args___] /; !TrueQ[inSym[sym]] :=
Internal`InheritedBlock[{inSym},
inSym[sym] = True;
f[sym, {args}]
];
Protect[protected]
]
],
syms
]
]
withCurried[fixedParams__][affectedFunctions___Symbol] := Function[
code
,
Internal`InheritedBlock[{affectedFunctions},
redefine[
{affectedFunctions},
Function[{s, a}, s[fixedParams, Sequence @@ a]]
];
code
]
,
HoldAll
]
The first function takes a number of symbols and a function, that takes the symbol, the argument passed to that symbol, and then uses that to do arbitrary computation with those.
The second function takes a number of fixed argument values in the first group of arguments, and a number of symbols in the second, and creates a dynamic environment where all these symbols are redefined such that these fixed arguments are automatically prepended to the list of passed arguments when these functions are called.
Illustration
Let's assume we want to fix the size argument value to be 3, and we want all 3 functions from the example above, to be affected. We create a dynamic environment, and save it in a variable:
dynenv = withCurried[3][diag, identity, norms]
We are now ready to use it:
dynenv[{
diag[10],
identity[],
norms[
Function[x, HermiteH[#, x]] &,
Function[{l, r}, Integrate[l[y]*r[y]* Exp[-y^2], {y, -Infinity, Infinity}]
]
}]
(*
{
{{10, 0, 0}, {0, 10, 0}, {0, 0, 10}},
{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}},
{{2 Sqrt[\[Pi]], 0, 0}, {0, 8 Sqrt[\[Pi]], 0}, {0, 0, 48 Sqrt[\[Pi]]}}
}
*)
You can see that now we have omitted the first parameter (size) in the calls to all these functions, yet they got the value 3 automatically passed to them.
You can create as many dynamic environments as you want, with different values of parameters (size in this case) embedded into them. The enviroments only affect the code that runs inside them. The global definitions of functions diag, identity, norms did not change.
This seems to be a more economical solution that creating new symbols in some new contexts. The only problematic case I can see for this approach is when you need to use these functions with different settings of size within the same piece of code.
Internal`InheritedBlock(3). The former was used to redefine functions in question (in such a way that allows them calling their old definitions without getting into infinite recursion), and the latter was used to make all these redefinitions local to the dynamic scope of executed code. – Leonid Shifrin Jun 20 '19 at 02:00protected = Unprotect[sym]andProtect[protected]are not essential for the operation of the code, in the sense that while they make the code more complete, they could also have been left out (particularly if none of the redefined symbols has theProtectedattribute). They just allow one to redefine alsoProtectedsymbols (whose definitions can't be changed without firstUnprotect-ing them), and if the symbol has been originally protected, reconstruct back theProtectedattribute after the symbol has been redefined. – Leonid Shifrin Jun 20 '19 at 02:09Function[code, body, HoldAll]acts like a macro, injecting unevaluatedcodeinto thebodyand then evaluatingbody. This is exactly equivalent to taking the actual code you pass into it and manually inserting it into thebodyfor every occurrence of thecodevariable, and 2. I have modified the standard VG construct to use a single variableinSymto guard all functions at once, and so instead of the standardBlock[{inFunction=True}, ...]I useInternal`InheritedBlock[{inSym}, inSym[sym]=True; ...]. – Leonid Shifrin Jun 20 '19 at 02:15