I am having a headache trying to put together a simple functionality.
Consider the following definitions:
type Entity = {Id:int;Data:string}
type IRepository =
abstract member SaveAsync: array<Entity> -> Task<bool>
abstract member RollBackAsync: array<Entity> -> Task<bool>
type INotification =
abstract member SaveAsync: array<Entity> -> Task<bool>
The use Task<T> because they are libraries developed in other .NET languages.
(I have created this code for the sake of the example)
Basically, I want to save data in the repository service, and then save the data in the notification service. But if this second operation fails, and that includes exceptions, I want to rollback the operation in the repository. Then there are two situations where I would want to call the rollback operation, the first if notification.SaveAsync returns false, and the second if it throws an exception. And of course, I would like to code that call to rollback once, but I cannot find the way.
This is what I have tried:
type Controller(repository:IRepository, notification:INotification) =
let saveEntities entities:Async<bool> = async{
let! repoResult = Async.AwaitTask <| repository.SaveAsync(entities)
if(not repoResult) then
return false
else
let notifResult =
try
let! nr = Async.AwaitTask <| notification.SaveAsync(entities)
nr
with
| _-> false
if(not notifResult) then
let forget = Async.AwaitTask <| repository.RollBackAsync(entities)
return false
else
return true
}
member self.SaveEntitiesAsync(entities:array<Entity>) =
Async.StartAsTask <| saveEntities entities
But unfortunately I get a compiler error on the let! nr = ... saying: This construct may only be used within computation expressions
Which would be the right way of doing this?
The problem is that when you use let v = e in computation expressions, the expression e is an ordinary expression that cannot contain further asynchronous constructs. That's exactly what happens here:
let notifResult =
try
let! nr = Async.AwaitTask <| notification.SaveAsync(entities)
nr
with _-> false
You can turn this into a nested async block:
let! notifResult = async {
try
let! nr = Async.AwaitTask <| notification.SaveAsync(entities)
return nr
with _-> return false }
Related
In F# async workflows, we can define a resource that should be cleaned up with the use keyword.
But how does use interact with return?
For example, given this code:
let createResource = async {
use r = Resource ()
do! operationThatMightThrow r
return r
}
async {
use! r = createResource
printfn "%O" r
}
|> Async.RunSynchronously
Where will the calls to Resource.Dispose happen?
How can I design this so that the r is always cleaned up (even if operationThatMightThrow throws)?
I usually have two solutions.
The first solution is actively capturing the exception, manually disposing the disposable object, and re-throwing the exception:
let createResource = async {
let r = new Resource ()
try do! operationThatMightThrow r
with e -> (r :> IDisposable).Dispose(); raise e
return r
}
The second solution is to use a continuation function that will have access to the disposable object before the async returns:
let createResource cont = async {
use r = new Resource ()
do! operationThatMightThrow r
return cont r
}
async {
let! x = createResource (fun r -> printfn "in cont: %O" r)
...
}
They will occur before the value is returned from the computation expression, semantically they will happen in a finally block. If you want to look at the source of the generated using statement, you can find it here. It effectively generates an access-controlled dispose function that calls Dispose() on the resource you pass in, then makes an asynchronous try-finally block with that function in the finally clause.
I have two asynchronous operations, so I have these functions:
// Returs Async<Person>
let GetPerson =
...
// Returs Async<Address>
let GetAddress =
...
What is the idiomatic way to execute them in parallel and get their results?
My starting point is this approach.
let MyFunc = async {
let! person = GetPerson()
let! address = GetAddress()
...
}
This works, but this runs the two operations sequentially.
I also tried this (sort of based on my C# experience).
let MyFunc = async {
let personA = GetPerson()
let addressA = GetAddress()
let! person = personA
let! address = addressA
...
}
But it doesn't work, it also runs the two operations sequentially.
What most of the documentation says is to use Async.Parallel with a sequence, but the problem is that the result type of the two operations are different, so I cannot put them in a sequence.
let MyFunc = async {
let personA = GetPerson()
let addressA = GetAddress()
[personA; addressA]
|> Async.Parallel
...
}
This gives a compilation error, because the two values have different types. (And also, with this syntax, how could I get the actual results?)
What is the idiomatic way to do this?
The idiomatic approach is to start both computations using Async.StartAsChild and then wait for their completion using a second let!:
let MyFunc = async {
let! personWork = GetPerson() |> Async.StartChild
let! addressWork = GetAddress() |> Async.StartChild
let! person = personWork
let! address = addressWork
// (...)
}
Just calling GetPerson does not actually start the work - unlike in C# where tasks are created started, F# workflows are just descriptions of the work to be done, so they need to be started explicitly. The Async.StartChild operation gives you a workflow that starts the work and returns another workflow that can be used for wait for its completion.
My objective is to
start a GUI effect,
await some async work without freezing the GUI
do a final GUI effect
I've prepared a first demo code using a viewmodel with the following
member this.RunSetStatus() =
async {
this.Status <- "!Start resetting #" + DateTime.Now.ToString "yy.MM.dd hh:mm:ss"
let! task = async {
do! Async.Sleep (10 * 1000)
return "!Reset done #" + DateTime.Now.ToString "yy.MM.dd hh:mm:ss"
}
this.Status <- task
} |> Async.StartImmediate
It behaves as expected so I'm happy with the above.
The issue is when I replace the Sleep in the demo with real blocking work, like a wcf consumer, retrieving some results.
member this.CheckReport(user : string) =
async {
let endpoint = new ServiceEndpoint(ContractDescription.GetContract(typeof<IClaimService>),
new BasicHttpBinding(),
new EndpointAddress(address))
let factory = new ChannelFactory<IClaimService>(endpoint)
let channel = factory.CreateChannel()
let resp = channel.CheckReport(user)
factory.Close()
return resp
}
called from my final delegate command
let RefreshLogic() =
this.RefreshIsActive <- true
async {
let cons = ConsumerLib.ConsumerWCF()
let! task, msg = async {
try
let! resp = cons.CheckReport(Environment.UserName.ToLower())
return resp , ""
with
|exc -> return [||], (ConsumerLib.FindInner(exc).Message + ConsumerLib.FindInner(exc).StackTrace)
}
this.Reports <- task
this.RefreshIsActive <- false
this.StatusMsg <- msg
this.ExportCommand.RaiseCanExecuteChanged()
} |> Async.StartImmediate
It unfortunately freezes the GUI while refreshing (why?)
The problem is your CheckReport function. While it's an async block, it never actually calls any asynchronous work (ie: nothing is bound via let! or do!), so the entire block runs synchronously.
Even though the work is inside of an asynchronous workflow, when you use StartImmediate, the work runs synchronously up to the first actual asynchronous function call, which would be bound by let! or do!. Since your work is completely synchronous, this propogates upwards, and ends up being synchronous, blocking the UI.
If your WCF bindings were setup to include asynchronous versions that are Task returning, the best approach here would be to use the asynchronous version of the WCF method, which would look something like:
let! resp = channel.CheckReportAsync(user) |> Async.AwaitTask
As far as I understood, the use keyword disposes the bound IDisposable as soon it is out of scope, so considering this recursive function:
let rec AsyncAcceptMessages(client : WebSocket) =
async {
let! message = client.AsyncReadMessage
use reader = new StreamReader(message)
let s = reader.ReadToEnd()
printf "%s" <| s
do! AsyncAcceptMessages client
}
Let's pretend that the compiler does not find a way of using tail recursion, would that StreamReader be disposed after each recursion?
UPDATE
Tomas response show me a way of fixing it when you actually expect something back, but what if you are expecting nothing? like in this example with the StreamWriter:
let rec AsyncAcceptMessages(client : WebSocket) =
async {
let! message = client.AsyncReadMessage
if(not(isNull message)) then
let s =
use reader = new StreamReader(message)
reader.ReadToEnd()
use writer = new StreamWriter(client.CreateMessageWriter(WebSocketMessageType.Text), Encoding.UTF8)
writer.Write s
printf "%s" <| s
do! AsyncAcceptMessages client
}
As you're saying, the StreamReader would only be disposed of after the execution returns from the recursive call (i.e. never).
There is another issue, which is that do! is not treated as a tail-recursive call, so if you want to create an infinite tail-recursive loop, you need to use return! (otherwise your code will be leaking memory).
In this case, you can fix it easily using, because you're not doing any asynchronous operations with the StreamReader, so you can just create an ordinary local scope:
let rec AsyncAcceptMessages(client : WebSocket) =
async {
let! message = client.AsyncReadMessage
let s =
use reader = new StreamReader(message)
reader.ReadToEnd()
printf "%s" <| s
return! AsyncAcceptMessages client
}
If you wanted to call e.g. AsyncReadToEnd, then you could do something like:
let rec AsyncAcceptMessages(client : WebSocket) =
async {
let! message = client.AsyncReadMessage
let! s =
async { use reader = new StreamReader(message)
return! reader.ReadToEnd() }
printf "%s" <| s
return! AsyncAcceptMessages client
}
I have been trying to learn F# for the past couple of day and I keep running into something that perplexes me. My "learning project" is a screen scraper for some data I'm kind of interested in manipulating.
In F# PowerPack there is a call Stream.AsyncReadToEnd. I did not want to use the PowerPack just for that single call so I took a look at how they did it.
module Downloader =
open System
open System.IO
open System.Net
open System.Collections
type public BulkDownload(uriList : IEnumerable) =
member this.UriList with get() = uriList
member this.ParalellDownload() =
let Download (uri : Uri) = async {
let UnblockViaNewThread f = async {
do! Async.SwitchToNewThread()
let res = f()
do! Async.SwitchToThreadPool()
return res }
let request = HttpWebRequest.Create(uri)
let! response = request.AsyncGetResponse()
use responseStream = response.GetResponseStream()
use reader = new StreamReader(responseStream)
let! contents = UnblockViaNewThread (fun() -> reader.ReadToEnd())
return uri, contents.ToString().Length }
this.UriList
|> Seq.cast
|> Seq.map Download
|> Async.Parallel
|> Async.RunSynchronously
They have that function UnblockViaNewThread. Is that really the only way to asynchronously read the response stream? Isn't creating a new thread really expensive (I've seen the "~1mb of memory" thrown around all over the place). Is there a better way to do this? Is this what's really happenening in every Async* call (one that I can let!)?
EDIT: I follow Tomas' suggestions and actually came up with something independent of F# PowerTools. Here it is. This really needs error handling, but it asynchronous requests and downloads a url to a byte array.
namespace Downloader
open System
open System.IO
open System.Net
open System.Collections
type public BulkDownload(uriList : IEnumerable) =
member this.UriList with get() = uriList
member this.ParalellDownload() =
let Download (uri : Uri) = async {
let processStreamAsync (stream : Stream) = async {
let outputStream = new MemoryStream()
let buffer = Array.zeroCreate<byte> 0x1000
let completed = ref false
while not (!completed) do
let! bytesRead = stream.AsyncRead(buffer, 0, 0x1000)
if bytesRead = 0 then
completed := true
else
outputStream.Write(buffer, 0, bytesRead)
stream.Close()
return outputStream.ToArray() }
let request = HttpWebRequest.Create(uri)
let! response = request.AsyncGetResponse()
use responseStream = response.GetResponseStream()
let! contents = processStreamAsync responseStream
return uri, contents.Length }
this.UriList
|> Seq.cast
|> Seq.map Download
|> Async.Parallel
|> Async.RunSynchronously
override this.ToString() = String.Join(", ", this.UriList)
I think that AsyncReadToEnd that just synchronously calls ReadToEnd on a separate thread is wrong.
The F# PowerPack also contains a type AsyncStreamReader that contains proper asynchronous implementation of stream reading. It has a ReadLine method that (asynchronously) returns the next line and only downloads a few chunks from the source stream (using the asynchronous ReadAsync as opposed to running on a background thread).
let processStreamAsync stream = async {
use asyncReader = new AsyncStreamReader(stream)
let completed = ref false
while not (!completed) do
// Asynchrnously get the next line
let! nextLine = asyncReader.ReadLine()
if nextLine = null then completed := true
else
(* process the next line *) }
If you want to download the whole content as a string (instead of processing it line-by-line), then you can use ReadToEnd method of AsyncStreamReader. This is a proper asynchronous implementation that starts downloading block of data (asynchronously) and repeats this without blocking.
async {
use asyncReader = new AsyncStreamReader(stream)
return! asyncReader.ReadToEnd() }
Also, F# PowerPack is open-souorce and has permissive license, so the best way to use it is often to just copy the few files you need into your project.