F# Async cancellation define atomic computations - f#

I have cases like the following:
async {
let! result = props.onClick args
do someThingLikeShowToastMessageWithResult(result)
return result
}
The async computation is not started by me, but by a library over which I have no control, and this library (correctly) passes in a cancellation tokent to the eventual StartImmediate call.
How could I write this async computation in a way that it is 'atomic'? eg if a containing async computation gets cancelled, it is either cancelled before this, or after this, but not inside this computation.
The intention behind this is that once the onClick async computation is called (which is a server call saving data) the data is sent over, and the server will save it bar other errors. Cancelling here will only result in cancelling the notification of the user that the server did what he told it to do.
Edit: the best I could figure out is to break the Async CE with some other construct (eg Task, or in this case Promise as this is in a Fable app):
async {
let ct = new System.Threading.CancellationTokenSource()
let computation =
async {
let! result = props.onClick args
do someThingLikeShowToastMessageWithResult(result)
return result
}
let atomicComputation =
Async.StartAsPromise(computation, token = ct.Token)
|> Async.AwaitPromise
return! atomicComputation
}
Seems pretty convoluted, and am not yet sure of all the unintended consequences.

I also can't think of a better approach, the only thing I would do is add some helper function to make your intent a little clearer.
module Async =
let runUncancellable a =
async {
let ct = new System.Threading.CancellationTokenSource()
return! Async.StartAsTask(a, cancellationToken = ct.Token) |> Async.AwaitTask
}
Usage:
let someAsyncOperation : Async<int> =
failwith "NYI"
async {
let! a = someAsyncOperation
let! b = runUncancellable someAsyncOperation
let! c = someAsyncOperation
return a + b + c
}

Related

Use the TResult from the Task<TResult> in F#

I'm publishing events to an Azure Event Hub with an F# script. The equivalent C# code is as follows:
var connectionString = "<< CONNECTION STRING FOR THE EVENT HUBS NAMESPACE >>";
var eventHubName = "<< NAME OF THE EVENT HUB >>";
await using (var producer = new EventHubProducerClient(connectionString, eventHubName))
{
using EventDataBatch eventBatch = await producer.CreateBatchAsync();
eventBatch.TryAdd(new EventData(new BinaryData("First")));
eventBatch.TryAdd(new EventData(new BinaryData("Second")));
await producer.SendAsync(eventBatch);
}
I don't think the following is the best idiomatic F# although it works:
let producerClient = EventHubProducerClient(connectionString, eventHubName)
let cancellationToken = CancellationToken()
let eventDataBatch =
cancellationToken
|> producerClient.CreateBatchAsync
let edb = eventDataBatch.Result
edb.TryAdd event
producerClient.SendAsync edb
note: I've not included the code to create the event but it's a JSON string.
How can I avoid the call to Result? This looks like a step that could be much cleaner.
You can rewrite code that uses C# async/await using the task { .. } computation expression in F#. Inside this, you can use let! in place of await, but also do! for awaiting tasks that do not return result and use! for awaiting IDisposable results.
I'm not sure what library are you using here, but something like this illustrates the syntax:
let connectionString = "<< CONNECTION STRING FOR THE EVENT HUBS NAMESPACE >>";
let eventHubName = "<< NAME OF THE EVENT HUB >>";
let processEvent() = task {
use producer = new EventHubProducerClient(connectionString, eventHubName)
use! eventBatch = producer.CreateBatchAsync()
eventBatch.TryAdd(EventData(new BinaryData("First")))
eventBatch.TryAdd(EventData(new BinaryData("Second")))
do! producer.SendAsync(eventBatch) }
If you then call processEvent(), the result will again be Task. You can ignore that (and let the computation run), or wait for the result of that, if you intend to block at the top level.

F# how to stop Async.Start

Hi I have a question about async in F#.
So I have a simple procedure that runs in background that is placed in a member of a type and it looks like:
type Sender() =
member this.Start(udpConectionPool) = async {
(* Some operation that continuously sends something over udp*)
} |> Async.Start
So this starts and begins to continuously sends frames over UDP without blocking rest of the program, but from time to time i want to restart thread (let us say i want to add new endpoint it would send it to that is udpConnectionPool parameter).
I was thinking about something like dumping task to member and then:
member this.Stop() = async {
do! (*stop async start member that contains task*)
}
And then I can restart this task with updated connection pool, but I don't know if I can do that.
My question is, Is it possible to stop such task, or if not is there a better way to do it?
The standard way of cancelling F# async workflows is using a CancellationToken. When you call Async.Start, you can provide a cancellation token. When the token gets cancelled, the async workflow will stop (after the current blocking work finishes):
open System.Threading
let cts = new CancellationTokenSource()
let work = async { (* ... *) }
Async.Start(work, cts.Token)
cts.Cancel() // Sometime later from another thread
To integrate this with the Sender, you could either store the current CancellationTokenSource and have a Stop method that cancels it (if you want to keep this inside a stateful class). Alternatively, you could return IDisposable from the Start method in a way that is similar to how the Observable interface work:
type Sender () =
member this.Start(udpConnectionPool) =
let cts = new CancellationTokenSource()
let work = async { (* ... *) }
Async.Start(work, cts.Token)
{ new System.IDisposable with
member x.Dispose() = cts.Cancel() }
This way, the caller of Start is responsible for storing the returned IDisposable and disposing of it before calling Start again.

What is the equivalent of EF C# ToListAsync in F#?

I have the following code in C#:
public async Task<List<Table>> GetRows()
{
return await db.Table.ToListAsync();
}
where db is an EF 6 DbContext.
How do I write the equivalent thing in F# assuming that I access the same DbContext?
I came up with this code but I am stuck:
let getRows = async {
let q = query {
from r in db.Table
select r
}
q |> Seq.map(fun row -> ...) // <-- Here I might want to do some custom function
}
Thank you
There's no problem in using .ToListAsync() in F#. I assume that the point of your confusion is to make C# await play nicely with F# async.
In this case, you should use Async.AwaitTask
Here's a code
async {
let! q = db.Table.ToListAsync() |> Async.AwaitTask //here you have a C# list
}
I generally use TaskBuilder.fs, if most of what I'm doing is going to be using c# tasks. I find this preferable to converting back and forth between Async and Task, but it depends on the apis being used.
add from Nuget
dotnet add package TaskBuilder.fs --version 2.1.0
open and use task
open FSharp.Control.Tasks.V2
task {
let! q = db.Table.ToListAsync()
}

Asynchronous computation in F#

Let client be an instance of System.Net.Http.HttpClient.
In the following code
var response = await client.PostAsync(url, content);
processResponse(response);
there is no thread block between the first and the second line of code, so if we are in the UI thread, the UI remains responsive during the POST round-trip.
What is the F# code to obtain the same non-blocking behaviour? Is
let response = client.PostAsync(url, content) |> Async.AwaitTask |> Async.RunSynchronously
processResponse(response)
the correct code? I haven't clear whether RunSynchronously is blocking the current thread. If so, how do we obtain the same non-blocking behaviour as await?
EDIT
Maybe a little more context would help.
I have a Visual Studio solution with 2 projects: a WPF/WinForms app and a F# library, referenced by the app. The library provides a function/method, named FSLongWork(), which executes a long I/O operation, e.g. an HTTP GET or POST to a remote server using HttpClient.GetAsync/PostAsync, and returns a string.
The app front-end is a simple window with a button and a label. The button click handler must:
1) Call FSLongWork() in the F# library
2) Write in the Label a content that depends on the string returned in step 1.
Of course step 1 must occur asynchronously, to preserve UI responsiveness.
POSSIBLE C# app SOLUTION
F# library:
let FSLongWork() =
async {
do! Async.Sleep(5000);
return "F#"
} |> Async.StartAsTask
C# app click handler
private async void button1_Click(object sender, EventArgs e) {
var s = await FSLongWork();
label1.Text = s;
}
C# app button handler registration
this.button1.Click += new System.EventHandler(this.button1_Click);
POSSIBLE F# app SOLUTION
F# library:
let FSLongWork() =
async {
do! Async.Sleep(5000);
return "F#"
}
F# app click handler
let button1_Click (sender : obj) e =
async {
let! s = FSLongWork()
label1.Text <- s
}
F# app button handler registration
button1.Click.Add(RoutedEventHandler(fun sender e -> button1_Click sender e |> Async.StartImmediate)
The problem I see is that the F# library function (FSLongWork) is different in the two solutions (|> Async.StartAsTask is only in the first), which is not good in term of reusability.
We can use the first implementation in F# (change let! s = FSLongWork() to let! s = FSLongWork() |> Async.AwaitTask).
And the second implementation can be used in C# (change var s = await FSLongWork(); to var s2 = await Microsoft.FSharp.Control.FSharpAsync.StartAsTask(FSLongWork(), null, null);).
Yet it looks a bit awkward to me:
the natural F# implementation would be the second (without Async.StartAsTask), but this requires to reference Microsoft.FSharp and the use of the rather ugly Microsoft.FSharp.Control.FSharpAsync.StartAsTask(FSLongWork(), null, null); in the C# app.
On the other hand, the first implementation (with Async.StartAsTask), leads to a more natural use in C# (simply await FSLongWork()), but implies am async->Task->async round-trip when used by a F# app
Is there a way to write the F# library so that a C# user doesn't need to reference FSharp.Core and without influencing how the F# function is implemented?
see here: https://learn.microsoft.com/en-us/dotnet/fsharp/tutorials/asynchronous-and-concurrent-programming/async
Async.RunSynchronously will start an async workflow on another thread and await its result.
Async.Start will start an async workflow on another thread, and will not await its result.
So in this case:
async {
let! response = client.PostAsync(url, content) |> Async.AwaitTask
processResponse response
} |> Async.Start
In terms of good integration I would expect to be able to do as follows:
F# library:
let FSLongWork() =
async {
do! Async.Sleep(5000);
return "F#"
}
C# app click handler
private async void button1_Click(object sender, EventArgs e) {
var s = await FSLongWork();
label1.Text = s;
}
In other words, I would expect that the conversion between Async<> and Task<> was automatic/implicit when I use await in C#.
I thought that this was possible and that I wasn't able to find the way to do it, hence my questions.
Apparently, though, it is not possible and some manual conversion plumbing is required (as for example in the the possible solutions I reported).
Maybe it could be material for a future feature request.

F# syntax for async controller methods in ASP.NET Core

I'm new to F# and trying to translate some C# ASP.NET Core code into F#
There is a C# controller here, and a working translated F# controller here
even though I got it working, I can't seem to figure out how to make the controller actions async. The methods call async code on a Commands object and a Queries object that are injected. The Commands and Queries are currently implemented in C#.
So for example a couple of the async C# controller methods are:
public async Task<IEnumerable<ToDoItem>> Get()
{
return await queries.GetAll();
}
[HttpGet("{id}", Name = "GetTodo")]
public async Task<IActionResult> GetById(string id)
{
var item = await queries.Find(id);
if (item == null)
{
return NotFound();
}
return new ObjectResult(item);
}
public async Task<IActionResult> Create([FromBody] ToDoItem item)
{
if (item == null)
{
return BadRequest();
}
if (string.IsNullOrEmpty(item.Id)) item.Id = Guid.NewGuid().ToString();
await commands.Add(item);
return CreatedAtRoute("GetTodo", new { id = item.Id }, item);
}
and I've translated those to F# like this:
[<HttpGet>]
member __.Get() =
__.Queries.GetAll() // this should be awaited
[<HttpGet("{id}", Name = "GetFSTodo")>]
member __.GetToDoItem(id) =
let data = __.Queries.Find(id) // this should be awaited
if isNull data
then __.NotFound() :> IActionResult
else
new ObjectResult(data) :> IActionResult
[<HttpPost>]
member __.Create([<FromBody>] item:ToDoItem) =
item.Id <- Guid.NewGuid().ToString()
(__.Commands.Add(item)) |> ignore // this should be awaited
let rv = new RouteValueDictionary()
rv.Add("id",item.Id)
__.CreatedAtRoute("GetTodo", rv, item) :> IActionResult
These methods work but I think they are not correctly done since they aren't awaiting the async calls on Queries and Commands. I've thrashed for some hours with trial and error but every attempt I've made to make the controller methods async results in them not returning any data to the browser even though they return a 200 status code. You can see some of my attempts commented out in the F# controller
Hoping some F# guru(s) could help me translate those methods correctly. There are some pretty bad tooling issues currently in terms of F# with ASP.NET Core which makes it more difficult for a newbie like me. I've mentioned those issues in the readme
There are a few additional methods in the code but I figure if I can learn how to solve for these methods then the same solution will probably apply to the other methods.
The code is in a public repository so you can easily try it in VS 2015 as long as you have the latest VS updates and the latest ASP.NET Core tooling installed
UPDATE:
thanks to the linked post by Mark Seemann, I was able to get this method working async
[<HttpGet("{id}", Name = "GetFSTodo")>]
member __.GetToDoItem(id) =
async {
let! data = __.Queries.Find(id) |> asyncReturn
if isNull data
then return __.NotFound() :> IActionResult
else
return new ObjectResult(data) :> IActionResult }
|> Async.StartAsTask
by using the helper function
let asyncReturn x = async { return x }
I'm still struggling with this method
[<HttpGet>]
member __.Get() =
async {
let! data = __.Queries.GetAll() |> asyncReturn
return data }
|> Async.StartAsTask
which is translated from this C# method:
[HttpGet]
public async Task<IEnumerable<ToDoItem>> Get()
{
return await queries.GetAll();
}
the async F# method works but it produces different json output than the C# version
C#
[{"id":"4f4e1596-6a48-4854-9982-7a2568aa1b1b","title":"add input validation","isDone":false,"dateAdded":"2016-09-20T21:16:04.8791044Z"},{"id":"9929b657-6a53-40b6-8c1c-1e4d0db593cd","title":"make F# controller async","isDone":false,"dateAdded":"2016-09-21T19:36:44.6650776Z"},{"id":"5bb5f544-6289-4051-ad65-d0dc016128e7","title":"learn F# basics","isDone":true,"dateAdded":"2016-09-22T11:59:00"},{"id":"e5e06118-c49f-496a-8175-9719ea72beed","title":"monkey business","isDone":false,"dateAdded":"2016-09-22T16:22:20.3133161Z"},{"id":"af0db8f2-6b49-4e31-86fa-e27c8e091f42","title":"funky bidness","isDone":false,"dateAdded":"2016-09-22T16:23:35.1175195Z"}]
F#
{"result":[{"id":"4f4e1596-6a48-4854-9982-7a2568aa1b1b","title":"add input validation","isDone":false,"dateAdded":"2016-09-20T21:16:04.8791044Z"},{"id":"9929b657-6a53-40b6-8c1c-1e4d0db593cd","title":"make F# controller async","isDone":false,"dateAdded":"2016-09-21T19:36:44.6650776Z"},{"id":"5bb5f544-6289-4051-ad65-d0dc016128e7","title":"learn F# basics","isDone":true,"dateAdded":"2016-09-22T11:59:00"},{"id":"e5e06118-c49f-496a-8175-9719ea72beed","title":"monkey business","isDone":false,"dateAdded":"2016-09-22T16:22:20.3133161Z"},{"id":"af0db8f2-6b49-4e31-86fa-e27c8e091f42","title":"funky bidness","isDone":false,"dateAdded":"2016-09-22T16:23:35.1175195Z"}],"id":65,"exception":null,"status":5,"isCanceled":false,"isCompleted":true,"creationOptions":0,"asyncState":null,"isFaulted":false}
so I still could use some help on how to make the F# version produce the expected output
UPDATED 2016-09-28
Thanks to Ruben Bartelink this is what my controller looks like now correctly implemented as async and handling the nuances that differ between C# and F# async patterns:
namespace FSharp.WebLib
open System
open Microsoft.AspNetCore.Mvc
open Microsoft.AspNetCore.Routing
open Microsoft.AspNetCore.JsonPatch
open FSharp.Models
module ActionResult =
let ofAsync (res: Async<IActionResult>) =
res |> Async.StartAsTask
[<Route("api/[controller]")>]
type FSToDoController(commands: IToDoCommands, queries: IToDoQueries) =
inherit Controller()
[<HttpGet>]
member this.Get() =
ActionResult.ofAsync <| async {
let! data = queries.GetAll()
return JsonResult(data) :> _ }
[<HttpGet("{id}", Name = "GetFsToDo")>]
member this.Get(id) =
ActionResult.ofAsync <| async {
let! res = queries.Find id
match res with
| None -> return this.NotFound() :> _
| Some data -> return ObjectResult(data) :> _ }
// create
[<HttpPost>]
member this.Post([<FromBody>] item:ToDoItem) =
ActionResult.ofAsync <| async {
if not this.ModelState.IsValid then
return this.BadRequest() :> _
else
let item = { item with Id = Guid.NewGuid() |> string }
do! commands.Add item
let rv = RouteValueDictionary()
rv.Add("id",item.Id)
return this.CreatedAtRoute("GetFsToDo", rv, item) :> _ }
// update
[<HttpPut("{id}")>]
member this.Put(id:String, [<FromBody>] item:ToDoItem) =
ActionResult.ofAsync <| async {
if (not this.ModelState.IsValid) || String.IsNullOrEmpty item.Id then
return this.BadRequest() :> _
else
let! res = queries.Find id
match res with
| None -> return this.NotFound() :> _
| Some toDo ->
do! commands.Update item
return NoContentResult() :> _ }
for anyone else interested in learning F# particularly for use in ASP.NET Core, this is part of a proof of concept project on github that has both C# and F# implementations of a ToDo list back end web api both of which are consumed from a front end implemented with polymer web components. Models and data access are also implemented in both languages to provide a good comparison for C# devs like myself to learn F#

Resources