Question

How to iterate over ReadOnlySpan in F#?

You can get Spans via System.Memory to.NET Standard 2.0 compatible libraries.

But how do you do this the most efficiently in F#:

let myRsp = "test1".AsSpan()
let test = Seq.forall (fun x -> Char.IsLower x) myRsp 

ReadOnlySpan<char> seems not to be compatible with Seq/Array/List and doing .ToArray() is making a copy. And if you try something like seq { for c in myRsp do yield (Char.IsLower c) } |> Seq.forall id you cannot do it because seq { } is like a function and you cannot transfer ReadOnlySpan<char> easily between functions.

 5  80  5
1 Jan 1970

Solution

 5

Unfortunately, there's no good way to turn a ReadOnlySpan into an F# sequence and the compiler won't help paper over the difference (unlike the C# compiler). So, given those limitations, here's how I would do it:

module ReadOnlySpan =

    let forall predicate (source : ReadOnlySpan<_>) =
        let mutable state = true
        let mutable e = source.GetEnumerator()
        while state && e.MoveNext() do
            state <- predicate e.Current
        state

Test:

let myRsp = "test1".AsSpan()
let test = ReadOnlySpan.forall Char.IsLower myRsp
printfn "%A" test   // false

This isn't really much better than your solution, but it's a bit cleaner and mimics the F# core implementation of Seq.forall.

2024-07-04
Brian Berns

Solution

 1

Update: So far this is the best I've found:

let rec forAll (sub:inref<ReadOnlySpan<char>>) i =
    if i >= sub.Length then true
    else
    if Char.IsLower sub.[i] then
        forAll &sub (i+1)
    else false

let myRsp = "test1".AsSpan()
let test = forAll &myRsp 0
2024-07-04
Tuomas Hietanen