F# Range Specification Mistake
I’ve already mentioned how much I dislike F# for loops. I ran into an issue in the spec that affects loops as well any language construct that uses the range syntax expr1 .. expr2. The spec for ranges is this:
The default definition of these operators is in Microsoft.FSharp.Core.Operators. The (..) operator generates an IEnumerable<_> for the range of values between the given start (expr1) and finish (expr2) values, using an increment of 1 (as given by Microsoft.FSharp.LanguagePrimitives.GenericOne).
This is great – if you use the following code:
let x = [| 0..7 |];;
you get this:
val x : int [] = [|0; 1; 2; 3; 4; 5; 6; 7|]
which is what you’d expect. If you do this, on the other hand:
let x = [| 7..0 |];;
you get this:
val x : int [] = [||]
Empty array? Really? Well, yes – by the spec they say that they will construct an IEnumerable<_> and use an increment of 1. In reality, they should use an increment of sign(expr2 – expr1). This means that if I write the following function:
let f a b =
for i in a .. b do someOtherFunction i
I will only get predictable results is a < b. The workaround is to write it this way:
let f a b =
for i in a .. sign (b – a) .. b do someOtherFunction i
in order to get predictable results. Note that I do not want to do this:
let f a b =
for i in (min a b) .. (max a b) do someOtherFunction i
as that will always run low to high and not in the order asked
Range syntax is a nice bit of sugar. It’s a shame to see it so badly broken. I imagine that this was brought in from OCAML but the section of the spec (such as it is) that I found doesn’t proscribe the ordering of a range. I’d be interested in hearing if the F# implementation was accidental or not. It would certainly lighten the syntax by removing the need for downto in for loops.