I'm new to Xamarin, and am trying to build a simple Android app with F#. I'm trying to load in data from a REST API with async, and then display it. I understand that interacting with the UI must be done on the MainThread, and that there is something along the lines of Activity.RunOnUiThread(). I've tried the following:
let onSearch args =
let search = this.FindViewById<EditText>(Resource_Id.search)
let searchResults = this.FindViewById<TextView>(Resource_Id.searchResults)
button.Text <- search.Text
async {
let! results = recipeSearch.GetRecipes search.Text
searchResults.Text <- results
}
|> Async.Start
button.Click.Add onSearch
Which throws the Exception about interacting with the UI elements in another thread.
And this:
let result = async {
let! results = recipeSearch.GetRecipes search.Text
return results
}
|> Async.RunSynchronously
searchResults.Text <- result
Defeats the purpose of doing it Async
Thanks
Try this:
let onSearch args =
let search = this.FindViewById<EditText>(Resource_Id.search)
let searchResults = this.FindViewById<TextView>(Resource_Id.searchResults)
button.Text <- search.Text
async {
let! results = recipeSearch.GetRecipes search.Text
this.RunOnUiThread(fun () -> searchResults.Text <- results)
}
|> Async.Start
button.Click.Add onSearch
Related
Lets assume we have the following C# method
public async Task DoStuffAsync(string path)
{
var fullPath = Path.Combine(
Directory.GetCurrentDirectory(),
path
);
using var stream = File.OpenText(fullPath);
while (!stream.EndOfStream)
{
var line = await stream.ReadLineAsync();
await Console.Out.WriteLineAsync(line);
}
}
and we want to implement this in F#, my first attempt was to code it like this
let doStuffAsync path =
async {
let fullPath = Path.Combine(Directory.GetCurrentDirectory(), path)
use stream = File.OpenText fullPath
while (not stream.EndOfStream) do
let! line = stream.ReadLineAsync() |> Async.AwaitTask
System.Console.Out.WriteLineAsync(line) |> Async.AwaitTask |> ignore
}
but that does not seam right to me as we have to unpack a Task<string> into a Async<string> then unpack this. Same for the Task from WriteLineAsync.
Are there no F# variantes for the methods ReadLineAsync and WriteLineAsync which return Async<_> instead of Task<_>?
Also is there somekind of async pipe operaor that would let me do
stream.ReadLineAsync()
|> Async.AwaitTask
|> System.Console.Out.WriteLineAsync
|> Async.AwaitTask
|> ignore
The use of Async.AwaitTask is correct as #NghiaBui pointed out. These are two async models and you need to convert in between them.
I'm afraid there are no Async<'T> variants of ReadlineAsync and most .net core libraries.
You could use TaskBuilder for some cleaner syntax:
task {
let fullPath = Path.Combine(Directory.GetCurrentDirectory(), path)
use stream = File.OpenText fullPath
while (not stream.EndOfStream) do
let! line = stream.ReadLineAsync()
do! System.Console.Out.WriteLineAsync line
}
Regarding the piping, I'm afraid it's not possible. There are some discussions to bring in a |>! operator but it looks like it would add more complexity than it's necessary.
Let's say I have a synchronous expensive operation:
let SomeExpensiveOp():string=
System.Console.WriteLine"about to begin expensive OP"
System.Threading.Thread.Sleep(TimeSpan.FromSeconds 2.0)
System.Console.WriteLine"finished expensive OP"
"foo"
That I wrap as an async job:
let SomeExpensiveOpAs():Async<string>=async {
return SomeExpensiveOp()}
Now I want to use this expensive operation to combine it with other two:
let SomeExpensiveOpSeq():seq<Async<string>>=
let op = SomeExpensiveOpAs()
seq {
for some in [Bar(); Baz()] do
yield async {
let! prefix = op
let! someAfterWaiting = some
return (String.Concat (prefix, someAfterWaiting))
}
}
The purpose of putting it into a seq<Async<'T>> is to be able to use Async.Parallel this way:
let DoSomething() =
let someAsyncOps = SomeExpensiveOpSeq() |> List.ofSeq
let newOp = SomeExpensiveOpAs()
let moreAsyncOps = (newOp::someAsyncOps)
let allStrings = Async.RunSynchronously(Async.Parallel moreAsyncOps)
for str in allStrings do
Console.WriteLine str
Console.WriteLine()
However, this makes SomeExpensiveOp to be executed three times. I would expect the second time to be executed an extra time because of the newOp call above, but I was expecting SomeExpensiveOpSeq to reuse the call to SomeExpensiveOp instead of calling it twice. How can I achieve SomeExpensiveOpSeq to only call SomeExpensiveOp once and reuse that for subsequent results?
The key observation here is that let! is invoking the async expression every time—nothing caches its result. Consider this example where we have expOp : Async<string> but we await it three times in an async expression:
let expOp = SomeExpensiveOpAs()
async {
let! a = expOp
let! b = expOp
let! c = expOp
return [a;b;c]
} |> Async.RunSynchronously
about to begin expensive OP
finished expensive OP
about to begin expensive OP
finished expensive OP
about to begin expensive OP
finished expensive OP
val it : string list = ["foo"; "foo"; "foo"]
You can see the async expensive op gets evaluated each time. If you only want to execute that expensive operation once, you could fully evaluate/await its result and use that instead of awaiting it multiple times:
let SomeExpensiveOpSeq():seq<Async<string>>=
let op = SomeExpensiveOpAs() |> Async.RunSynchronously
seq {
for some in [Bar(); Baz()] do
yield async {
let! someAfterWaiting = some
return (String.Concat (op, someAfterWaiting))
}
}
This will still result in the expensive op being executed twice in your code—once in SomeExpensiveOpSeq and another as a result of being prepended on to moreAsyncOps—but it could be refactored further to a single invocation. Basically, if all subsequent async ops depend on this expensive evaluation, why not evaluate it once/first and then use its value wherever necessary:
let SomeExpensiveOpSeq op : seq<Async<string>>=
seq {
for some in [Bar(); Baz()] do
yield async {
let! someAfterWaiting = some
return (String.Concat (op, someAfterWaiting))
}
}
let DoSomething() =
let newOp = SomeExpensiveOpAs() |> Async.RunSynchronously
let someAsyncOps = SomeExpensiveOpSeq newOp |> Async.Parallel |> Async.RunSynchronously
let allStrings = newOp::(List.ofArray someAsyncOps)
for str in allStrings do
Console.WriteLine str
Console.WriteLine()
> DoSomething();;
about to begin expensive OP
finished expensive OP
foo
foobar
foobaz
I'm playing around with using SqlClient in F# and I'm having difficulty with using SqlDataReader.ReadAsync. I'm trying to do the F# equivalent of
while (await reader.ReadAsync) { ... }
What is the best way to do this in F#? Below is my full program. It works, but I'd like to know if there is a better way to do it.
open System
open System.Data.SqlClient
open System.Threading.Tasks
let connectionString = "Server=.;Integrated Security=SSPI"
module Async =
let AwaitVoidTask : (Task -> Async<unit>) =
Async.AwaitIAsyncResult >> Async.Ignore
// QUESTION: Is this idiomatic F#? Is there a more generally-used way of doing this?
let rec While (predicateFn : unit -> Async<bool>) (action : unit -> unit) : Async<unit> =
async {
let! b = predicateFn()
match b with
| true -> action(); do! While predicateFn action
| false -> ()
}
[<EntryPoint>]
let main argv =
let work = async {
// Open connection
use conn = new SqlConnection(connectionString)
do! conn.OpenAsync() |> Async.AwaitVoidTask
// Execute command
use cmd = conn.CreateCommand()
cmd.CommandText <- "select name from sys.databases"
let! reader = cmd.ExecuteReaderAsync() |> Async.AwaitTask
// Consume reader
// I want a convenient 'while' loop like this...
//while reader.ReadAsync() |> Async.AwaitTask do // Error: This expression was expected to have type bool but here has type Async<bool>
// reader.GetValue 0 |> string |> printfn "%s"
// Instead I used the 'Async.While' method that I defined above.
let ConsumeReader = Async.While (fun () -> reader.ReadAsync() |> Async.AwaitTask)
do! ConsumeReader (fun () -> reader.GetValue 0 |> string |> printfn "%s")
}
work |> Async.RunSynchronously
0 // return an integer exit code
There is one issue in your code which is that you're doing a recursive call using
do! While predicateFn action. This is a problem because it does not turn into a tail-call and so you could end up with memory leaks. The right way to do this is to use return! instead of do!.
Aside from that, your code works good. But you can actually extend the async computation builder to let you use ordinary while keyword. To do that, you need a slightly different version of While:
let rec While (predicateFn : unit -> Async<bool>) (action : Async<unit>) : Async<unit> =
async {
let! b = predicateFn()
if b then
do! action
return! While predicateFn action
}
type AsyncBuilder with
member x.While(cond, body) = Async.While cond body
Here, the body is also asynchronous and it is not a function. Then we add a While method to the computation builder (so we are adding another overload as an extension method). With this, you can actually write:
while Async.AwaitTask(reader.ReadAsync()) do // This is async!
do! Async.Sleep(1000) // The body is asynchronous too
reader.GetValue 0 |> string |> printfn "%s"
I'd probably do the same as you. If you can stomach refs though, you can shorten it to
let go = ref true
while !go do
let! more = reader.ReadAsync() |> Async.AwaitTask
go := more
reader.GetValue 0 |> string |> printfn "%s"
let failing = async {
failwith "foo"
}
let test () =
try
Async.Start(failing)
with
| exn -> printf "caught"
This code doesn't catch the exception. How can I start an asynchronous workflow on a separate thread and catch the exception in the main program?
as an alternative you start the workflow as a task and use it's methods and properties instead. For example Task.Result will rethrow an exception again so this works, and is almost what you tried:
let test () =
try
Async.StartAsTask failing
|> fun t -> t.Result
with _ -> printfn "caught"
run
> test ();;
caught
val it : unit = ()
on a differnt thread
sorry - I just saw that you want it on a different thread - in this case you most likely want to use the internal approach RCH gave you - but you could use ContinueWith too (although a bit ugly):
open System.Threading.Tasks
let test () =
(Async.StartAsTask failing).ContinueWith(fun (t : Task<_>) -> try t.Result with _ -> printfn "caught")
run
> test ();;
caught
val it : Task = System.Threading.Tasks.Task {AsyncState = null;
CreationOptions = None;
Exception = null;
Id = 3;
IsCanceled = false;
IsCompleted = true;
IsFaulted = false;
Status = RanToCompletion;}
without Async.Catch
also you don't really need the Async.Catch:
let test () =
async {
try
do! failing
with _ -> printfn "caught"
} |> Async.Start
As there is no result awaited, there is no place where the exception could be caught. You need to wrap the computation. One possibility:
let failing = async {
failwith "foo"
}
let test () =
async {
let! res = failing |> Async.Catch
match res with
| Choice1Of2 _ -> printf "success"
| Choice2Of2 exn -> printfn "failed with %s" exn.Message
} |> Async.Start
I wrote this little web listener simulation:
Agent.Start(fun (_ : MailboxProcessor<unit>) ->
let listener = new HttpListener()
listener.Prefixes.Add(addr)
listener.Start()
let rec respondOut() = async {
let! context = Async.FromBeginEnd(listener.BeginGetContext, listener.EndGetContext)
use s = context.Response.OutputStream
let wr = new StreamWriter(s)
use disp = { new IDisposable with
member x.Dispose() =
printfn "Disposing..."
wr.Dispose() }
wr.Write("Test")
return! respondOut()
}
respondOut()
)
I don't understand why Dispose is not called on disp on every loop?
As a side question, I'm doing all this because I want to test what is the proper behavior to respond text in a web service. I'm not sure if I should be doing:
use s = Context.Response.OutputStream
use sw = new StreamWriter(s)
sw.Write("test")
or
Context.Response.Write("Test")
Context.Response.End()
or whatnot.
Thanks!
When in doubt, use reflector :). The use keyword create the scope of "using" till then end of the block. When used inside the async workflow if you de-sugar the async keyword you will get something like:
Async.Bind(Async.FromBeginEnd(listener.BeginGetContext, listener.EndGetContext)
(fun context ->
use s = context.Response.OutputStream
let wr = new StreamWriter(s)
use disp = { new IDisposable with
member x.Dispose() =
printfn "Disposing..."
wr.Dispose() }
wr.Write("Test")
Async.ReturnFrom ( respondOut() )
)
Now the call Async.ReturnFrom at last will continue calling the function recursively and if you replace the use with " C# using() { } " where the } bracket is after the Async.ReturnFrom then the dispose will never get called
Wrapping the use part in a do block should solve the problem:
let rec respondOut() = async {
let! context = Async.FromBeginEnd(listener.BeginGetContext, listener.EndGetContext)
do
use s = context.Response.OutputStream
let wr = new StreamWriter(s)
use disp = { new IDisposable with
member x.Dispose() =
printfn "Disposing..."
wr.Dispose() }
wr.Write("Test")
return! respondOut()
}
use extends to the end of the block, so I would expect Dispose to be called after the recursive computation returns (which is never, in this case, since it loops unconditionally). If you want to dispose of the resource earlier, you'll need to delimit the scope of the use binding somehow. Perhaps something like this would work (I haven't tried it):
let rec respondOut() = async {
let! context = Async.FromBeginEnd(listener.BeginGetContext, listener.EndGetContext)
do! async {
use s = context.Response.OutputStream
let wr = new StreamWriter(s)
use disp = { new IDisposable with
member x.Dispose() =
printfn "Disposing..."
wr.Dispose() }
wr.Write("Test")
}
return! respondOut()
}
My guess is disp is optimized away in your compiled code since it isn't used. Try adding printfn "%A" disp on the next line.