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?
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)
() }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With