Is there an alternative to "let!" to support function chaining? - f#

Is there an alternative to "let!" to support function chaining?
I currently have this:
async {
let! result = token |> queryDay
result |> toTransactions
}
However, I would like to consolidate the above code into something like this:
async {
// NOTE: Async.Await isn't a thing
let result = token |> queryDay |> Async.Await |> toTransactions
}
Is there a way to achieve this?

From your examples I'm assuming that:
queryDay : 'a -> Async<'b>
toTransactions : 'b -> Async<'c>
Chaining monadic functions like that is called "bind". This is the core operation of a monad, the very essense of it. In fact the let! construct gets desugared into a call to async.Bind, and so does do!.
Unfortunately, the F# standard library doesn't offer a built-in standalone implementation of bind for Async. But there is one in FSharpx that you can use:
open FSharpx.Control
async {
let! result = token |> queryDay |> Async.bind toTransactions
}
Or if you don't want to use FSharpx, you can easily make one yourself by delegating to the computation builder:
module Async =
let bind f a = async.Bind(a, f)

Related

Reader monad transformer sample with FSharpPlus

I'm trying to understand the reader monad transformer. I'm using FSharpPlus and try to compile the following sample which first reads something from the reader environment, then performs some async computation and finally combines both results:
open FSharpPlus
open FSharpPlus.Data
let sampleReader = monad {
let! value = ask
return value * 2
}
let sampleWorkflow = monad {
do! Async.Sleep 5000
return 4
}
let doWork = monad {
let! envValue = sampleReader
let! workValue = liftAsync sampleWorkflow
return envValue + workValue
}
ReaderT.run doWork 3 |> Async.RunSynchronously |> printfn "Result: %d"
With this I get a compilation error at the line where it says let! value = ask with the following totally unhelpful (at least for me) error message:
Type constraint mismatch when applying the default type 'obj' for a type inference variable. No overloads match for method 'op_GreaterGreaterEquals'.
Known return type: Async
Known type parameters: < obj , (int -> Async) >
It feels like I'm just missing some operator somewhere, but I can't figure it out.
Your code is correct, but F# type inference is not that smart in cases like this.
If you add a type annotation to sampleReader it will compile fine:
let sampleReader : ReaderT<int,Async<_>> = monad {
let! value = ask
return value * 2
}
// val sampleReader : FSharpPlus.Data.ReaderT<int,Async<int>> =
// ReaderT <fun:sampleReader#7>
Update:
After reading your comments.
If what you want is to make it generic, first of all your function has to be declared inline otherwise type constraints can't be applied:
let inline sampleReader = monad ...
But that takes you to the second problem: a constant can't be declared inline (actually there is a way but it's too complicated) only functions can.
So the easiest is to make it a function:
let inline sampleReader () = monad ...
And now the third problem the code doesn't compile :)
Here again, you can give type inference a minimal hint, just to say at the call site that you expect a ReaderT<_,_> will be enough:
let inline sampleReader () = monad {
let! value = ask
return value * 2
}
let sampleWorkflow = monad {
do! Async.Sleep 5000
return 4
}
let doWork = monad {
let! envValue = sampleReader () : ReaderT<_,_>
let! workValue = liftAsync sampleWorkflow
return envValue + workValue
}
ReaderT.run doWork 3 |> Async.RunSynchronously |> printfn "Result: %d"
Conclusion:
Defining a generic function is not that trivial task in F#.
If you look into the source of F#+ you'll see what I mean.
After running your example you'll see all the constraints being generated and you'll probably noted how the compile-time increased by making your function inline and generic.
These are all indications that we're pushing F# type system to the limits.
Although F#+ defines some ready-to-use generic functions, and these functions can sometimes be combined in such a way that you create your own generic functions, that's not the goal of the library, I mean you can but then you're on your own, in some scenarios like exploratory development it might make sense.

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

F# Async.Map not found

I have source from http://sourceforge.net/p/onvifdm/code/HEAD/tree/
When I try to build this solution, I get the following issue in the onvif.utils project.
use! response = request.AsyncGetResponse() |> Async.Map(fun x-> x:?>HttpWebResponse)
Error: Method or object constructor 'Map' not found
D:\Working\onvifdm\onvifdm-code-96-trunk\onvif\onvif.utils\OdmSession.fs
Here's a function that extends Async, taken from a useful source on understanding map.
module Async =
let map f op = async {
let! x = op
let value = f x
return value
}
Which has a type signature ('a -> 'b) -> Async<'a> -> Async<'b>
For example -
let onInts = async { return 1 }
... is an async<int>, and ...
let onStrings = Async.map (fun x -> x.ToString()) onInts
... produces an Async<string>.
You can use following code as a replacement:
let! webResponse = request.AsyncGetResponse()
use response = webResponse:?>HttpWebResponse
Also you can write your own extension to Async type - here is an example how it can be done. All you need is to implement high order function for the code above.
For those still looking for an answer to this, the problem is that Async is referencing the wrong class. Change it to Microsoft.FSharp.Control.Async and the problem should be resolved.
if this is still an open question, let me suggest using FsToolkit.ErrorHandling:
[https://www.nuget.org/packages/FsToolkit.ErrorHandling]
It contains an Async.map implementation and a lot of useful features to deal with Result.
As an alternative I think this should work:
use! response = request.AsyncGetResponse() |> async.Bind (fun x-> x:?>HttpWebResponse |> async.Return)

The most appropriate way to return a hot, awaited task from F# to a caller in C# framework

It looks like if the following situation occurs often and I wonder
What would be the most appropriate and shortest F# equivalent (e.g. no extra tasks created, has the same SynchronizationContext etc.)?
Does the following have something that should be fixed or improved?
Here DoAsync is a member function derived from a framework class, it takes a parameter and returns a hot, awaited task to the caller that is some framework function.
In C#:
public async Task DoAsync(int x)
{
if(x == 10)
{
await taskContext.ReturnAsync();
}
}
Here Async.Ignore is from here
In F#:
member x.DoAsync(int x) =
async {
if x = 10
return! Async.AwaitTask(taskContext.ReturnAsync() |> Async.Ignore)
else
return! Async.AwaitTask(Task.FromResult(0))
} |> Async.StartAsTask :> Task
Look at Tomas' answer for a simpler way. As an added note, in F# 4.0 it looks like there's an overload for non-generic Task available. More details at this Visual F# Tools PR.
Using Async.AwaitTask and Async.StartAsTask is the way to go. Although you do not really need to return anything from the async if you just want to return a non-generic Task:
member x.DoAsync(x:int) =
let work = async {
if x = 10 then
do! taskContext.ReturnAsync() |> Async.Ignore }
Async.StartAsTask(work) :> Task

How do I use an async workflow in a Seq.pick in F#

I am new to functional programming in general and started learning F# recently. I wanted to use an async workflow returning Async<'U option> to pick an item in a Sequence. I find a nice Seq.pick function, but I am not sure how I could use that with an async workflow.
If that is not possible, is there another alternative to using an imperative style program to pick the item from the list. The following is a modified variation of my program. Any feedback is highly appreciated.
let run = async {
while not stopped do
use! resource = acquireResourceLockAsync
let! items = fetchItemsAsync 5
let! item = Seq.pick returnIfLocked items
let! status = performTaskAsync item
do! updateStatusAsync status
do! Async.Sleep 1000
}
Thanks in anticipation.
EDIT: Updated my question based on the answer by jpalmer. I noticed both Seq.filter and Seq.pick earlier and decided that Seq.pick will meet my need better, as I need the first item that I am able to lock. However, I forgot to change the return value of my function - instead of returning true, it should return Some(item). Now with that update, is there an elegant way to approach this without 1) blocking a thread to convert Async<'U option> to 'U and 2) resorting to an imperative style looping?
I am unclear exactly what you are trying to do. If you want to convert from Async<'T> to 'T non-blocking, then you want to use let! in an async workflow. So the seq-like logic probably needs to be written as its own loop, as suggested below. If that doesn't help, then perhaps share more code, especially the intended types of items/item/returnIfLocked, as I'm unclear what's async in your example.
let asyncPick f (s:seq<_>) =
async {
use e = s.GetEnumerator()
let r = ref None
while Option.isNone(!r) && e.MoveNext() do
let! x = f e.Current
r := x
match !r with
| Some z -> return z
| None -> return failwith "no matching item found"
}
let chooser ax =
async {
let! x = ax
if x%3 = 0 then
return Some x
else
return None
}
let s = seq { for i in 1..10 do yield async { return i } }
let main() =
async {
let! firstChosen = s |> asyncPick chooser
return firstChosen
}
|> Async.RunSynchronously
|> printfn "%d"
main()
It is important to look at the signature of the function you are using,
Seq.pick expects a function which returns option<'t>, you want to use Seq.Filter which takes a function which returns a bool.
You will still have another problem though in that you have Async<bool> - you will need to convert that to a normal bool, but you could do this inside your 'Seq.Filter' function

Resources