how to abort a seq in F#?

Viewed 92

I have a seq like this:

let data =
    seq {
        for i in source do
            do some stuff...
            yield result
    }

But I would like to turn it into a Result object and, during the iteration I have some conditions that mean an error. For example:

let data =
    seq {
        for i in source do
            do some stuff...
            if a > b then .. let's abort everything and return error
            yield result
    } |> Ok

The calculations are quite expensive, so it'd prefer not to wrap the yield with Option / Result and sort it out later if I can abort early.

Or, if I do that, how could I iterate until it either completes, or returns an error?

How can this be done?

1 Answers

You can pipe to Seq.takeWhile and sort out there if the Seq should be aborted:

let data =
    source
    |> Seq.map (fun s -> ... do some stuff ...)
    |> Seq.takeWhile (fun r -> ... return false if error ...)

Seq<T> is IEnumerable<T> so it is lazy, calculations are done when new items are pulled out of it.


Edit:

To know if there was an error or if we reached the end of the seq I would traverse it using a fold - so that a seq of "source" items are calculated on the fly and if there is an error then pass through the rest without running the calculation. Here's an example:

let source = seq { 
  1
  2
  3
  4 }

let expensiveCalc item =
  printfn "expensiveCalc called with %i" item
  if item > 2 then Error (sprintf "%i is too high" item)
  else Ok item

let traverse valuesResult newSourceItem =
  valuesResult
  |> Result.bind (fun values ->
      expensiveCalc newSourceItem
      |> Result.map (fun newResult -> newResult::values))

let endResult =
  source
  |> Seq.fold traverse (Ok [])

match endResult with
| Ok values -> printfn "%A" values
| Error error -> printfn "%s" error

As you can see any number greater than 2 is considered an error, and when running it stops calling expensiveCalc after the first error.

Related