Here's a tighter version, I'm sure you can improve it more.
#light
let rec is_prime n =
let limit = System.Math.Sqrt(float(n)) |> int
let rec is_prime primes =
match primes with
| LazyList.Cons(p,tail) when (p>limit) ->
(*printfn "p=%d and limit=%d. Returning true" p limit;*) true
| LazyList.Cons(p,tail) ->
//printfn "Checking if %d is divisible by prime %d" n p
if (n=p) then (*printfn "It's equal to p, so it's prime";*) true
else if (n%p=0) then (*printfn "It's divisible, so not prime";*) false
else is_prime tail
| _ -> failwith "impossible, primes are infinite list"
is_prime primes
and next_prime_after n =
assert(n%2=1)
let candidate = n + 2
//printfn "Checking if %d is prime" candidate
if (is_prime candidate) then
//printfn "It is"
candidate
else
//printfn "It's not, moving on..."
next_prime_after candidate
and all_primes_after n =
let next = next_prime_after n
LazyList.consf next (fun () -> all_primes_after next)
and primes = LazyList.consf 2 ( fun() -> LazyList.consf 3 ( fun() -> all_primes_after 3 ) )
primes |> LazyList.take 10 |> Seq.to_list |> printfn "%A"
The warning about initialization soundness is just telling you that it will check for bad programs such as this one:
let Call f = f ()
let rec Kaboom = Call (fun () -> 1 + Kaboom)
which tries to evaluate Kaboom inside the definition of Kaboom. 'primes' is similar, except we don't evaluate primes now, we just store it away in a thunk inside the lazy list tail, so it will only be evaluated after the initial value of 'primes' has been set.