# Avoid hardcoding of variables for Compile by using a list

I want to automatically Compile an expression in between a routine of certain analytical manipulations. The number of arguments for the function may change for every run, so I would like to avoid hardcoding the arguments into my call of Compile. Consider this MWE:

vars = {x, y};
expr = Sin[x + y];
fun1 = With[{e = expr}, Compile[{{x, _Real}, {y, _Real}}, e]];


The disadvantage here is that I need to provide {{x, _Real}, {y, _Real}} as variables explicitly. I would rather like to be able to do something like

fun2 = With[{e = expr, v = Transpose[{vars, ConstantArray[_Real, Length@vars]}]},
Compile[v, e]]


where knowing vars in advance allows me to avoid manually computing v, print it and then copy-paste into Compile. All variants of fun2 I could come up with boil down to the issue that Compile only recognizes a single variable as input. Is there any way to achieve the desired functionality?

This can be accomplished through Hold[] trickery:

vars = {x, y}; expr = Sin[x + y];

fun1 = Hold[Compile][Transpose[PadRight[{vars}, {2, Automatic}, {_Real}]],
expr] // ReleaseHold;


An alternative is to start with a Hold[] expression, inject any needed changes with With[], and then use Apply[]:

fun1 = With[{vlist = Transpose[PadRight[{vars}, {2, Automatic}, {_Real}]]},
Compile @@ Hold[vlist, expr]];


Test:

fun1[π/6, π/3]
1.

• better perhaps to start with a HoldComplete than a List Mar 29 '18 at 5:02
• Hmm, I don't think it works with Hold[] or HoldComplete[] for the second version, because the part that assembles the sequence of arguments won't fire inside a Hold[], unless you MapAt[] an Evaluate[]. Mar 29 '18 at 5:04
• I meant something like With[{vars=varList}, Compile@@Hold[vars, expr]]. Allows you to keep expr held. Mar 29 '18 at 5:28
• Yes, injection should work; I'll edit in a bit. Mar 29 '18 at 5:29
• @J.M. Thanks a lot. I clearly need to get used to Hold a lot more. Nice and compact solution. Side question: Is there a specific reason why you are using the PadRight based way to generate the list of variables instead of my way? Is it just personal taste or is there more behind it? Mar 29 '18 at 6:32

Often I do things like this with Replace as a destructuring function. Here's a super convoluted example that uses Replace, Hold, and Thread to define a Compile spec that holds all its arguments and auto-detects its variables (also not gonna lie it was kinda fun to write):

Options[compileWithVars] =
Join[
{
"TypeMap" -> Automatic
},
Options[Compile]
];
compileWithVars[
varList : {__Symbol} | Automatic : Automatic,
expr_,
ops___?OptionQ
] :=
With[
{
varsHeld =
Replace[Hold[varList],
{
Hold[Automatic] :>
DeleteDuplicates@
Cases[Hold[expr],
s_Symbol?(Function[Null, Context[#] == \$Context,
HoldFirst]) :> Hold[s],
Infinity
],
}
],
tm =
Replace[
Lookup[Flatten@{ops}, "TypeMap", Automatic ],
Automatic :> {_ -> {_Real}}
]
},
Replace[
Map[Replace[Join[#, Hold @@ (# /. tm)], Hold[l__] :> Hold[{l}]] &,
varsHeld ], Hold],
{
Hold[vl : {__List}] :>
Apply[
Compile,
HoldComplete[
vl,
expr,
Evaluate@FilterRules[
Flatten@{ops},
Options[Compile]
]
]
],
sheesh_ :>
<|

"MessageTemplate" :> "Var spec  is invalid (originally )",
"MessageParameters" :> {sheesh,
|>
]
}
]
]


Then we do it like so:

compileWithVars[Sin[x + y]]


Or we can tell it that we expect x to be a {_Integer, 1} and everything else to default to {_Real, 2}:

compileWithVars[z*Cos[θ]*Sin[x + y],
"TypeMap" -> {Hold[x] -> {_Integer, 1}, _ -> {_Real, 2}}]


• Might be a useful addition: ReduceFreeVariables[] will allow you to extract the variables from an arbitrary expression. Mar 29 '18 at 6:00
• @J.M. Ooh very useful. Can it extract them in a held manner? Mar 29 '18 at 6:01
• Haven't tried yet, sorry. Mar 29 '18 at 6:05
• @J.M. seems it doesn't dig into things wrapped in Hold which is too bad. I'd prefer if it did but just returned that variables in held form. Mar 29 '18 at 6:53
• @user49047 did you do compileWithVars[expr, {"RuntimeAttributes" -> {Listable}, "Parallelization" -> True}]`? That seems to work for me. Apr 4 '18 at 15:43