# Strange Behavior of Compile[] inside With[]

Consider, in the examples below, the difference between defining $$n$$ as a global variable or as an argument to function comp[], which includes a compiled function that depends on a constant defined in an encompassing With[] which depends on $$n$$.

1. If I evaluate
ClearAll[comp, n]

n = 3;

comp = With[{x = n^2},
Compile[{}, x]];

then running comp[] outputs 9. This is fine.

1. If I evaluate
ClearAll[comp, n]

comp = With[{x = n^2},
Compile[{{n, _Integer}}, x]];

then running comp[3] outputs 9. This is fine.

1. If I evaluate
ClearAll[comp, n]

n = 3;

comp = With[{x = Array[#^2 &, n]},
Compile[{}, x]];

then running comp[] gives me {1,4,9}. This is fine.

1. However, if I evaluate
ClearAll[comp, n]

comp = With[{x = Array[#^2 &, n]},
Compile[{{n, _Integer}}, x]];

I get the error "Array: Single or list of non-negative machine-sized integers expected at position 2 of Array[#1^2&,n]". The output to comp[3] is still {1,4,9} (however, in more complicated forms of example 4, the output is not what it should be).

Example 1 is to example 3 as example 2 is to example 4.

Why do examples 1-3 not give an error but example 4 does?

The error is thrown by Array, not by compile. With has to evaluate the code for x first and this is why Array throws the error.

• Compile[] is an important part though, is it not? For example, no error is thrown in either f[n_] := With[{x = Array[#^2 &, n]}, 2] or f = With[{x = Array[#^2 &, n]}, Function[n, 2]] (f[3], for example, throws no error). If you mean the error is "Array:..." then I agree; I mentioned this in my question. – Just Some Old Man Aug 7 '20 at 16:13
• f = With[{x = Array[#^2 &, n]}, Function[n, 2]] does throw the same error if n is undefined. – Henrik Schumacher Aug 7 '20 at 16:53
• My bad, thank you. I forgot to clear n. – Just Some Old Man Aug 7 '20 at 17:48
• What would be the best way to pass n as an argument to comp without throwing the error? (Essentially, I'd like to get the behavior of example 3 but define n as an argument, to comp, not as a global variable). I know I can do something like comp[n_] := With[{x = Array[#^2 &, n]}, f = Compile[{}, x]; f[]], and comp[3] gives me the right answer with no error, but introducing f is a bit sloppy. Do you believe this is best? – Just Some Old Man Aug 8 '20 at 6:47
• Well, this error can easily be ignored. You can do comp = Quiet[With[{x = Array[#^2 &, n]}, Compile[{{n, _Integer}}, x]]];. But an easier way would be comp = Compile[{{n, _Integer}}, Array[#^2 &, n]]. Or comp = Compile[{{n, _Integer}}, Table[i^2 &, {i, 1, n}]]. – Henrik Schumacher Aug 8 '20 at 7:11

As explained by Henrik, the problem is that Array is being evaluated before it gets inserted into Compile. The solution here is to use SetDelayed to prevent its evaluation.

comp = With[{x := Array[#^2 &, n]}, Compile[{{n, _Integer}}, x]]
• This seems to work, but the function I am using suffered a massive drop in speed, probably because x is now being evaluated millions of times. – Just Some Old Man Aug 10 '20 at 9:04

Another way:

comp = With[{x = Unevaluated@Array[#^2 &, n]},
Compile[{{n, _Integer}}, x]]

By the way, Trace or TracePrint will show the order of evaluation, when there is a question about what is happening. When the code is short, like in this case, it can clarify what is going on.

TracePrint@With[{x = Array[#^2 &, n]}, Compile[{{n, _Integer}}, x]]

It also shows the desired code is injected for x, despite the error.

• I use a variation of this technique: I let body = Hold[Unevaluated[Array[#^2 &, n]]]; so that I can readily generate compiled and uncompiled versions of the same code: fCompiled = Compile[{{n, _Integer}}, #] & @@ body and fUncompiled = Function[{n}, #] & @@ body. – J. M.'s torpor Aug 9 '20 at 14:11