Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use while loop in F# Async expression?

Tags:

f#

c#-to-f#

How would I go about writing the following in F#?

var reader = await someDatabaseCommand.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
  var foo = reader.GetDouble(1);
  // ... 
}

I'm very new to F# so this is the best I can come up with

task { 
  let! reader = someDatabaseCommand.ExecuteReaderAsync ()
  let! tmp1 = reader.ReadAsync ()
  let mutable hasNext = tmp1
  while hasNext do
    // ...
    let! tmp2 = reader.ReadAsync ()
    hasNext <- tmp2
}

I know it's a C# library so it's not going to be perfect, but I'd like to avoid needing to store the result of ReadAsync in a variable, let alone have two temporaries. I don't know of a way to get the "result" of a Task without binding it to a name. Perhaps a recursive solution would work?

like image 636
Adam Avatar asked Dec 06 '25 17:12

Adam


1 Answers

This library works very nicely with C#, because you can use await in the condition of the loop. I think that using recursive computation expression in F# is a bit nicer than the imperative solution (I do not think you can simplify that), but it is about the same length:

task {
  let! reader = someDatabaseCommand.ExecuteReaderAsync ()
  let rec loop () = task { 
    let! hasNext = reader.ReadAsync ()
    if hasNext then
      // ..
      return! loop () }
  return! loop ()
}

A more elaborate solution would be to use F# asynchronous sequences (from the FSharp.Control.AsyncSeq library). I think this only works for async (and so you'd have to use that instead of task), but it makes the code nicer. You can define a function that runs a command and returns an asynchronous sequence of results (for simplicity, I just return the reader, which is a bit dirty, but works):

#r "nuget: FSharp.Control.AsyncSeq"
#r "nuget: System.Data.SqlClient"
open FSharp.Control

let runCommand (cmd:System.Data.SqlClient.SqlCommand) = asyncSeq { 
  let! reader = cmd.ExecuteReaderAsync () |> Async.AwaitTask
  let rec loop () = asyncSeq { 
    let! hasNext = reader.ReadAsync () |> Async.AwaitTask
    if hasNext then
      yield reader
      yield! loop () }
  yield! loop () }

Given this, you can very nicely iterate over the results using plain for loop inside async (using an overload added by the AsyncSeq library):

async { 
  for reader in runCommand someDatabaseCommand do
    let foo = reader.GetDouble(1) 
    () }
like image 189
Tomas Petricek Avatar answered Dec 09 '25 01:12

Tomas Petricek



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!