F# - Writing a function that accepts both non-generic and generic argument - f#

I defined await function like this:
let await (task : Task<'a>) =
task |> Async.AwaitTask |> Async.RunSynchronously
and two tasks like this:
let stringTask = new Task<string>(fun () -> "something")
let unitTask = new Task(fun () -> printf "something")
Calling them like this:
let stringResult = await stringTask
await unitTask
But the second is not generic and I can't call await for it, thus I edited the function argument like this:
let await (task : Task) =
task |> Async.AwaitTask |> Async.RunSynchronously
The problem is now the await stringTask is returning unit instead of string.
How should I write the await function to accept both Task<'a> and Task as the parameter and await it?

Your original definition for await is fine.
The problem is with unitTask which is not generic. Make it generic the same way you did with stringTask:
let unitTask = new Task<unit>(fun () -> printf "something")
If you want to handle both generic Task<'a> and non-generic Task, you have 2 options.
Create 2 await functions (the functional way):
let await (task : Task<'a>) = task |> Async.AwaitTask |> Async.RunSynchronously
let awaitUnit (task : Task ) = task |> Async.AwaitTask |> Async.RunSynchronously
or create 2 Await members for instance on type Task (the OO way):
type Task with
static member Await (task : Task ) = task |> Async.AwaitTask |> Async.RunSynchronously
static member Await (task : Task<'a>) = task |> Async.AwaitTask |> Async.RunSynchronously
By convention members use Pascal case so I used Await instead of await.
Remember to call task.Start() before you call your await.

Async.AwaitTask already has overloads for both Task<'a> and Task, so unless you really need a helper function, you can use it directly:
let stringResult = Async.AwaitTask stringTask |> Async.RunSynchronously
Async.AwaitTask unitTask |> Async.RunSynchronously
And if this is your actual code and you really need to block the thread to wait for those results, you can also just use the Task API directly to save the Task -> Async translation overhead:
let stringResult = stringTask.Result
unitTask.Wait()

You could create a Task -> Task<unit> helper function to convert Task objects you get from the API into Task<unit>. I suspect that this isn't the best way to do it, since it has been a while since I've paid much attention to the differences between F# async and the framework's Task-based asynchrony, but this works in a trivial extension of your example:
open System.Threading
open System.Threading.Tasks
let toUnitTask (t : Task) =
new Task<_>(fun () -> t.Start(); t |> Async.AwaitTask |> Async.RunSynchronously)
let await (task : Task<_>) =
task |> Async.AwaitTask |> Async.RunSynchronously
let stringTask = new Task<string>(fun () -> Thread.Sleep 3000; "something")
let unitTask = new Task(fun () -> Thread.Sleep 2000; printfn "something") |> toUnitTask
stringTask.Start()
unitTask.Start()
let stringResult = await stringTask
await unitTask

Related

await generic Task and return the result

I have function call
let result = session.Query<Domain.CustomerReadModel>().ToListAsync()
which returns Task<Collections.Generic.IReadOnlyList<Domain.CustomerReadModel>>
How do I "await" this task correctly in F#?
I tried
async {
session.Query<Domain.CustomerReadModel>().ToListAsync() |> ignore
}
as well as
let result = session.Query<Domain.CustomerReadModel>().ToListAsync()
|> Async.AwaitTask
|> Async.RunSynchronously
This seems to compile but I can't use the result and return it:
session.Query<Domain.CustomerReadModel>().ToListAsync()
|> Async.AwaitTask
|> Async.RunSynchronously
Async.AwaitTask takes the task, wraps it in an async computation, and returns you that computation. Once you have it, you can use it with let! or do! or return! just like any other async computation.
async {
let queryAsFsharpAsync = session.Query<Domain.CustomerReadModel>().ToListAsync() |> Async.AwaitTask
let! result = queryAsFsharpAsync
...
}
Or without giving the computation its own name:
async {
let! result = session.Query<Domain.CustomerReadModel>().ToListAsync() |> Async.AwaitTask
...
}

Calling C# method which returns a Task

This is some C# code:
var streamStore = new PostgresStreamStore(new PostgresStreamStoreSettings("Host=localhost;Port=5432;User Id=postgres;Password=123456;Database=postgres"));
await streamStore.CreateSchemaIfNotExists();
I'm trying to call it from F# like this:
let db_connection =
Sql.host "localhost"
|> Sql.port 5432
|> Sql.username "postgres"
|> Sql.password "123456"
|> Sql.database "postgres"
|> Sql.str
let store =
new PostgresStreamStore(PostgresStreamStoreSettings(db_connection))
store.CreateSchemaIfNotExists() |> Async.AwaitTask |> ignore
The code compiles, however the schema in contrast to the C# Version does not a create a schema.
How do I await this Task from store.CreateSchemaIfNotExists?
I'm getting this error message:
`This expression is a function value, i.e. is missing arguments. Its type is unit -> Tasks.Task.
In the C# code, you are using await, so this must be inside an async method. The corresponding thing in F# would be to use F# asynchronous workflows. Inside those, you can use let! which is similar to await. This works with computations of type Async<T> rather than Task<T>. The operation Async.AwaitTask turns Task<T> into Async<T> so that you can access it using let!
let doSomething () = async {
let db_connection =
Sql.host "localhost"
// (other configuration omitted)
let store =
new PostgresStreamStore(PostgresStreamStoreSettings(db_connection))
let! res = store.CreateSchemaIfNotExists() |> Async.AwaitTask
return "whatever" }
I assume that CreateSchemaIfNotExists does not return anything useful, so you can also wait for its completion using do!
do! store.CreateSchemaIfNotExists() |> Async.AwaitTask |> Async.Ignore
An asynchronous computation then needs to be started using Async.Start or Async.RunSynchronously, which is akin to starting a task or blocking using task.RunSynchronously.

How to do await an Async method, similar to C#

How to do an simple await in F# ?
In C# I have code like this:
await collection.InsertOneAsync(DO);
var r = collection.ReplaceOneAsync((fun d -> d.Id = DO.Id), DO)
So I created a let await = ... to my F# code become more similar with my C# code.
My current F# code is this:
let awaits (t: Threading.Tasks.Task) = t |> Async.AwaitTask |> Async.RunSynchronously
let await (t: Threading.Tasks.Task<'T>) = t |> Async.AwaitTask |> Async.RunSynchronously
let Busca (numero) =
let c = collection.Find(fun d -> d.Numero=numero).ToList()
c
let Insere(DO: DiarioOficial) =
//collection.InsertOneAsync(DO) |> Async.AwaitTask |> Async.RunSynchronously
collection.InsertOneAsync(DO) |> awaits
let Salva (DO: DiarioOficial) =
//let r = collection.ReplaceOneAsync((fun d -> d.Id = DO.Id), DO) |> Async.AwaitTask |> Async.RunSynchronously
let r = collection.ReplaceOneAsync((fun d -> d.Id = DO.Id), DO) |> await
r
I want to have only one definition for await (awaits), but the best I could do is this, because on Insere, type is Task, but on Salva, type is Task<'T>
If i use only the await, I get this compile error:
FS0001 The type 'Threading.Tasks.Task' is not compatible with the type 'Threading.Tasks.Task<'a>'
If I use only the awaits, it compiles, but I lose the return type from the async Task
I want to merge the await and awaits in a single
let await = ...
How can I do this?
In F# we tend to use another syntax. It is described e.g. here: https://fsharpforfunandprofit.com/posts/concurrency-async-and-parallel/.
or here: https://learn.microsoft.com/en-us/dotnet/fsharp/tutorials/asynchronous-and-concurrent-programming/async
The idea of working with C# Tasks is to "convert" them to async with Async.Await<'T>
You can do it probably another way, but it is the most straightforward.
There are two parts of writing async code in both F# and C#.
You need to mark the method or code block as asynchronous. In C#, this is done using the async keyword. The F# equivalent is to use the async { ... } block (which is an expression, but otherwise, it is similar).
Inside async method or async { .. } block, you can make non-blocking calls. In C#, this is done using await and in F# it is done using let!. Note that this is not just a function call - the compiler handles this in a special way.
F# also uses Async<T> type rather than Task<T>, but those are easy to convert - e.g. using Async.AwaitTask. So, you probably want something like this:
let myAsyncFunction () = async {
let! _ = collection.InsertOneAsync(DO) |> Async.AwaitTask
let r = collection.ReplaceOneAsync((fun d -> d.Id = DO.Id), DO)
// More code goes here
}
I used let! to show the idea, but if you have an asynchronous operation that returns unit, you can also use do!
do! collection.InsertOneAsync(DO) |> Async.AwaitTask

Rewriting C# code using Task.WhenAll in F#

I have the following interface method:
Task<string[]> GetBlobsFromContainer(string containerName);
and its implementation in C#:
var container = await _containerClient.GetContainer(containerName);
var tasks = container.ListBlobs()
.Cast<CloudBlockBlob>()
.Select(b => b.DownloadTextAsync());
return await Task.WhenAll(tasks);
When I try to rewrite it in F#:
member this.GetBlobsFromContainer(containerName : string) : Task<string[]> =
let task = async {
let! container = containerClient.GetContainer(containerName) |> Async.AwaitTask
return container.ListBlobs()
|> Seq.cast<CloudBlockBlob>
|> Seq.map (fun b -> b.DownloadTextAsync())
|> ??
}
task |> ??
I'm stuck with the last lines.
How to return to Task<string[]> from F# properly?
I had to guess what the type of containerClient is and the closest I found is CloudBlobClient (which does not have getContainer: string -> Task<CloubBlobContainer> but it shouldn't be too hard to adapt). Then, your function might look like as follows:
open System
open System.Threading.Tasks
open Microsoft.WindowsAzure.Storage.Blob
open Microsoft.WindowsAzure.Storage
let containerClient : CloudBlobClient = null
let GetBlobsFromContainer(containerName : string) : Task<string[]> =
async {
let container = containerClient.GetContainerReference(containerName)
return! container.ListBlobs()
|> Seq.cast<CloudBlockBlob>
|> Seq.map (fun b -> b.DownloadTextAsync() |> Async.AwaitTask)
|> Async.Parallel
} |> Async.StartAsTask
I changed the return type to be Task<string[]> instead of Task<string seq> as I suppose you want to keep the interface. Otherwise, I'd suggest to get rid of the Task and use Async in F#-only code.
Will this work?
member this.GetBlobsFromContainer(containerName : string) : Task<string seq> =
let aMap f x = async {
let! a = x
return f a }
let task = async {
let! container = containerClient.GetContainer(containerName) |> Async.AwaitTask
return! container.ListBlobs()
|> Seq.cast<CloudBlockBlob>
|> Seq.map (fun b -> b.DownloadTextAsync() |> Async.AwaitTask)
|> Async.Parallel
|> aMap Array.toSeq
}
task |> Async.StartAsTask
I had to make some assumptions about containerClient etc. so I haven't been able to test this, but at least it compiles.

When calling an F# async workflow, can I avoid defining a temporary label?

Suppose I have an async data source:
let getData() = async { return [ 3.14; 2.72 ] }
I could call it using let! and a temporary label:
let showData1() = async {
let! data = getData()
data
|> Seq.iter (printfn "%A")
}
Or, I could call it (inefficiently!) using Async.RunSynchronously and piping, and without a temporary label:
let showData2() = async {
getData()
|> Async.RunSynchronously
|> Seq.iter (printfn "%A")
}
I like the syntax of showData2 but know that calling Async.RunSynchronously ties up the calling thread.
Is there a syntax, operator, or function defined somewhere that allows me to pipe an Async<'T> into a function that accepts 'T?
It sounds like you want map for Async:
let mapAsync f a = async {
let! v = a
return (f v)
}
then you can do:
let m: Async<unit> = getData() |> mapAsync (Seq.iter (printfn "%A"))

Resources