# Bizarre behavior of With, Compile and Break

I'm guessing this is almost certainly a bug in Compile (because they're fairly common), but it's pretty weird and I spent about an hour tracking down the issue and finding a test case. Consider this function, which is obviously contrived, but does what you'd expect:

f = Function[xs,
Module[{i = 1},
While[i < Length@xs,
With[{x = xs[[i]]},
If[x < 5.0,
Break[],
++i]]];
i]];

f@Range[10.0, 1.0, -1.0]

(* 7 *)


OK, so my contrived function is not only contrived, it's also slow. I'll compile it so I can do useless things faster:

cf = Compile[{{xs, _Real, 1}},
Module[{i = 1},
While[i < Length@xs,
With[{x = xs[[i]]},
If[x < 5.0,
Break[],
++i]]];
i]];

cf@Range[10.0, 1.0, -1.0]

(* Hold[Break[]] *)


Oh, and I also got this rather goofy message:

Break::nofwd: No enclosing For, While, or Do found for Break[].

I can work around this by eliminating the With and substituting in by hand:

cf2 = Compile[{{xs, _Real, 1}},
Module[{i = 1},
While[i < Length@xs,
If[xs[[i]] < 5.0,
Break[],
++i]];
i]];

cf2@Range[10.0, 1.0, -1.0]

(* 7 *)

-
I guess this is even shorter but is the same basic problem of type inference. –  halirutan Apr 17 '14 at 21:27
@halirutan I was not aware of that particular link, although apparently took part in a discussion linked to by that link. In any case, that was long ago, and I had no memory of that one. Thanks for the reminder! –  Leonid Shifrin Apr 17 '14 at 21:32
@halirutan I just read Szabolcs's answer in more detail, and it is funny how similar our arguments are. To the point that this question might almost be considered a duplicate (but I think it is still a little short of being one). –  Leonid Shifrin Apr 17 '14 at 21:39
@halirutan I saw that, and didn't really think about it enough to realize it was probably the same issue. –  Pillsy Apr 17 '14 at 21:44
@Pillsy And that's why I think this is not really a duplicate, although a strongly related question for sure. –  Leonid Shifrin Apr 17 '14 at 21:45

The answer (if it can be called one) seems to be rooted in type-inferencing. The first observation you can make is that Break[] is considered a top-level function, rather than a statement, and is wrapped in a Function to be executed by main evaluator (in byte-code instructions):

Function[{xs}, If[CompileFunctionVariable\$38938 < 5., Break[], ++i]]


This is why you get an error message: Break[] is evaluated at the top-level, where of course there is no surrounding loop.

The real question is why such behavior. Here is my guess: since If returns a value, and so does With, the type-inferencer is trying to infer this value, even though later it is going to be discarded (it's not that clever to perform such an optimization during the type-inferencing stage). Now, you have this line:

If[x < 5.0, Break[], ++i]


This does not have a type which can be easily inferred. Then, the logic of the type-inferencer is to call the top-level code (main evaluator) on the smallest lexically closed expression containing this part. Perhaps this may be called a bug.

Here is a work-around:

If[x < 5.0, Break[];0, ++i]


even though 0 gets ignored at the end (since it is after Break[]), it gives the entire If a definite type of integer, which satisfies the type-inferencer and allows the code to be happily compiled. You can check that there are no more calls to the main evaluator in this case, and this version returns what you would expect.

-
+1 This issue is as awful as the type annotation with something like i=Most[{0}] inside Compile. Sometimes it would be better to cry difficulties out loud and document them instead of being silent and let users run onto ground. –  halirutan Apr 17 '14 at 21:38
@halirutan I can't agree more! –  Leonid Shifrin Apr 17 '14 at 21:40
Very good, +1 of course! Incidentally it would also be acceptable to put ++i; Null rather than Break[]; 0`. Your analysis is correct either way. –  Oleksandr R. Apr 17 '14 at 22:04
@OleksandrR. Thanks, good to hear! Particularly from you, given that you have an extensive knowledge of Compile -related matters. I'd be interested to dig deeper myself, but I never have the time for it. –  Leonid Shifrin Apr 17 '14 at 22:12