## 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 *expr _{1} .. expr_{2}*. 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 (expr) and finish (_{1}expr) values, using an increment of 1 (as given by Microsoft.FSharp.LanguagePrimitives.GenericOne)._{2}

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(expr_{2} – *expr _{1}*). 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.

## # re: F# Range Specification Mistake

While this is a small annoyance, you can correct it yourself easily. This is one of my favorite things about F# over C#, you almost always have the option to do it yourself if you don't like something that is already implemented.

So, the best solution looks like this:

let inline (..) a b =

if a < b then

seq { for x = a to b do yield x }

else

seq { for x = a downto b do yield x }

..and some tests..

let testUp () =

printf "testUp: "

for x in 0 .. 10 do

printf "%i " x

> testUp();;

testUp: 0 1 2 3 4 5 6 7 8 9 10 val it : unit = ()

let testDown () =

printf "testDown: "

for x in 10 .. 0 do

printf "%i " x

> testDown();;

testDown: 10 9 8 7 6 5 4 3 2 1 0 val it : unit = ()

I also thought this would be a fun opportunity to show off Seq.unfold. The following implementation works the same as above:

let inline (..) a b =

if a < b then

Seq.unfold (fun s -> if s <= b then Some(s, s + 1) else None) a

else

Seq.unfold (fun s -> if s >= b then Some(s, s - 1) else None) a