F-Sharp (F#) untyped infinity
-
21-09-2019 - |
Question
I wonder why F-Sharp doesn't support infinity.
This would work in Ruby (but not in f#):
let numbers n = [1 .. 1/0] |> Seq.take(n)
-> System.DivideByZeroException: Attempted to divide by zero.
I can write the same functionality in much complex way:
let numbers n = 1 |> Seq.unfold (fun i -> Some (i, i + 1)) |> Seq.take(n)
-> works
However I think that first one would be much more clear. I can't find any easy way to use dynamically typed infinity in F#. There is infinity keyword but it is float:
let a = Math.bigint +infinity;;
System.OverflowException: BigInteger cannot represent infinity. at System.Numerics.BigInteger..ctor(Double value) at .$FSI_0045.main@() stopped due to error
Edit: also this seems to work in iteration:
let numbers n = Seq.initInfinite (fun i -> i+1) |> Seq.take(n)
Solution
First of all, F# lists are not lazy, (I'm not sure Ruby lists are lazy), so even with a general notion of infinity your first example can never work.
Second, there is no infinity value in Int32. Only MaxValue. There is a positive and negative infinity in Double though.
Putting it together, this works:
let numbers n = seq { 1. .. 1./0. } |> Seq.take(n)
I feel however Seq.initInfinite is your best option. The code above looks strange to me. (Or at least use Double.PositiveInfinity instead of 1./0.)
At first sight, a nice option to have in the language would be an infinite range operator like in haskell: seq { 1.. } The problem is that it would only work for seq, so I guess the extra work to support postfix operators is not worth it for this feature alone.
Bottom line: in my opinion, use Seq.initInfinite.
OTHER TIPS
I think the following is the best solution to infinite ranges in F#; by marking the function inline
we do better than "dynamically typed infinity" we get structurally typed infinite ranges (works with int32, int64, bigint, ... any type that which has a static member +
which takes two arguments of its own type and returns a value of it's own type):
let inline infiniteRange start skip =
seq {
let n = ref start
while true do
yield n.contents
n.contents <- n.contents + skip
}
//val inline infiniteRange :
// ^a -> ^b -> seq< ^a>
// when ( ^a or ^b) : (static member ( + ) : ^a * ^b -> ^a)