According to the F# spec (see §6.5.7), simple for loops are bounded by integer (int
aka int32
aka System.Int32
) limits start
and stop
, e.g.
for i = start to stop do
// do sth.
I wonder why the iteration bounds for this type of for loop are required to be int32
. Why not allow uint32
? int64
? bigint
?
I'm aware that sequence iteration expressions (for ... in ...
) can iterate over arbitrary sequences; that however requires allocating an iterator and calling MoveNext
and Current
and what not and can thus be considerably less efficient than a plain loop could be (increment counter, compare, conditonal jump). To avoid that, you are stuck with using while
and a manually incrementing loop counters...
Strangely enough, F# does allow non-int32
loop bounds, if the for
expression is wrapped in a sequence expression, e.g.
seq { for i = 0I to 10I do
printfn "%A" i }
So, I guess the question is: Is there a particular reason for only allowing int32
for loops? And why does this restriction not apply to for
loops wrapped in seq
expressions?
I'm not sure why F# does not allow
int64
ranges. It sounds like a useful feature... (but I can understand thatint
is the standard type for this in C# and perhaps F# tries to follow this pattern).As for the workarounds, it is worth adding that you can also write
inline
higher-order function:...and then you can express
for
loops overint64
ranges in a fairly succinct way:I did a couple of experiments and it seems that the F# compiler is able to optimize this fairly decently (the lambda function is inlined and the tail-recursive
loop
function is turned into awhile
loop). I do not think this is guaranteed so you may need to check this by hand in high-performance code, but it seems to work fine for simpler examples.There is only one disadvantage - you won't be able to use local mutable variables (
let mutable
) because these cannot be captured by a lambda function. So there may be additional cost with indirectref
cells (but I'm not sure how big problem this is).If you want to keep the for-loop, there's a very simple work-around using the for...in loop with a sequence range operator:
The range operator will accept any integer of any size as long as both types match. For example, the following will not compile:
Another possible workaround:
[1L..100L] |> List.iter (fun i -> printfn "%i" i)