I have the following C# code:
_Client.Streams.InfoStream.Subscribe(async info =>
{
Console.WriteLine($"reconnection, message: {info.Info}, version: {info.Version:D}"));
await _Client.Send(new PingRequest());
...
}
and the following definitions are of interest:
public IObservable<InfoResponse> InfoStream => InfoSubject.AsObservable();
public static IDisposable Subscribe<T>(this IObservable<T> source, Action<T> onNext)
when I want to call the _Client.Streams.InfoStream.Subscribe method from F#, I do not know what the syntax should be:
Client.Streams.InfoStream
.Subscribe(
fun x -> ( printfn "%s" x.Info )
)
doesn't work. It tells me I'm returning an IDisposable type instead of a unit type. And this doesn't touch the async part yet.
what should the syntax be?
Edit:
Following the answer, here is the solution I came up with to run async code inside the callback:
Client.Streams.InfoStream
.Subscribe(
fun _ ->
(
async {
[
Client.Send(new PingRequest()) |> Async.AwaitTask;
Client.Send(new BookSubscribeRequest()) |> Async.AwaitTask;
Client.Send(new TradesSubscribeRequest()) |> Async.AwaitTask;
Client.Send(new AuthenticationRequest(id, key)) |> Async.AwaitTask
]
|> Async.Parallel
|> Async.Ignore
|> Async.Start
|> ignore
} |> ignore
)
) |> ignore
The Subscribe operation of an IObservable returns an IDisposable value that you can later use to cancel the subscription. In C#, you can just ignore return values, so C# implicitly treats this as an operation that returns void. In F#, you need to explicitly ignore results if you are not using them.
In your case, you probably need to write:
Client.Streams.InfoStream
.Subscribe(fun x -> printfn "%s" x.Info)
|> ignore
EDIT: In your edited example, I think you just want to start all the individual tasks. For this, you don't need any F# async. Assuming the Send method creates a "hot task" (i.e. a task that has already started), the following should work just fine:
Client.Streams.InfoStream.Subscribe(fun _ ->
Client.Send(new PingRequest()) |> ignore
Client.Send(new BookSubscribeRequest()) |> ignore
Client.Send(new TradesSubscribeRequest()) |> ignore
Client.Send(new AuthenticationRequest(id, key)) |> ignore
)
Related
I have the following code:
let exchangeInfo, marginBrackets, accountInfo =
async {
let failOrExtract = function
| Ok d -> d
| Error (e: ExchangeError) -> e.Describe |> tee logger.Fatal |> failwith
let pf = processResult >> failOrExtract
let! e = rest.FuturesUsdt.System.GetExchangeInfoAsync() |> Async.AwaitTask
let! b = rest.FuturesUsdt.GetBracketsAsync() |> Async.AwaitTask
let! i = rest.FuturesUsdt.Account.GetAccountInfoAsync() |> Async.AwaitTask
return pf e, pf b, pf i
} |> Async.RunSynchronously
In short, it calls 3 async rest functions, then sends them through failOrExtract which is made of 2 parts: the processResult part that returns a Result<'a, ExchangeError> type, with 'a different for each of these calls, and then failOrExtract that either fails or returns the data field.
this will not compile, the compiler says:
[FS0001] The type 'BinanceFuturesUsdtExchangeInfo' does not match the type 'System.Collections.Generic.IEnumerable<BinanceFuturesSymbolBracket>'```
It looks like the failOrExtract function was generic on the first use, but not afterwards since pf e compiles, but pf b and pf i have the same error.
why am I getting this error?
I think you're running into the F# "value restriction", although the compiler isn't being clear about it. The problem here is that pf is defined as a value, not a function, so the compiler is inferring that it has a concrete, non-generic type. In order to give pf a generic signature, try changing it to this instead:
let pf x = x |> processResult |> failOrExtract
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 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
I have the following code
open FSharp.Data
let downloadFile link =
......
use os = File.Create(...)
Http.RequestStream(....).ReponseStream.CopyTo(os)
let rec consume() = async {
......
|> Seq.iter (fun x ->
xxx |> Seq.iter(fun link ->
downloadFile link
))
}
I found that the sync downloading makes the code not run concurrently. So I'm trying to do somthing like the following. How to change it to use the FSharp.Data http AsyncRequestStream? Maybe the CopyTo can be async too?
open FSharp.Data
let downloadFile link = async {
......
use os = File.Create(...)
Http.AsyncRequestStream(....).ReponseStream.CopyTo(os) // Error
}
let rec consume() = async {
......
|> Seq.iter (fun x ->
xxx |> Seq.iter(fun link ->
downloadFile link |> Async.Start // do! downloadFile link????
))
}
consume() |> Async.RunSynchronously
Here's a skeleton solution, worthy of all the blank spots in your example:
let downloadFile link =
async {
......
use os = File.Create(...)
let! resp = Http.AsyncRequestStream(....)
return resp.ReponseStream.CopyTo(os)
}
let consume link =
async {
let comps : Async<unit> [] =
xxx
|> Seq.map (fun link -> downloadFile link)
|> Array.ofSeq
return! Async.Parallel comps
}
I think you should read up on asynchronicity and concurrency in general, as well as how to use it in F# in particular. From the OP it seems the whole thing is a bit hazy to you.
Edit: to answer the question in the comment:
With return! (or let!, or do!) you execute the nested workflow asynchronously, then pick up executing the current workflow from that point. That is, everything "below" the do! is put into a continuation that gets called once the thing "after" the do! finishes.
Whereas Async.Start fires up the workflow on (another) background thread and returns immediately without waiting for it to finish.
I am using the csv type provider to collect some data from a series of files I have on Azure blob storage:
#r "../packages/FSharp.Data.2.0.9/lib/portable-net40+sl5+wp8+win8/FSharp.Data.dll"
open FSharp.Data
type censusDataContext = CsvProvider<"https://portalvhdspgzl51prtcpfj.blob.core.windows.net/censuschicken/AK.TXT">
type stateCodeContext = CsvProvider<"https://portalvhdspgzl51prtcpfj.blob.core.windows.net/censuschicken/states.csv">
let stateCodes = stateCodeContext.Load("https://portalvhdspgzl51prtcpfj.blob.core.windows.net/censuschicken/states.csv");
let fetchStateData (stateCode:string)=
let uri = System.String.Format("https://portalvhdspgzl51prtcpfj.blob.core.windows.net/censuschicken/{0}.TXT",stateCode)
censusDataContext.Load(uri).Rows
let usaData = stateCodes.Rows
|> Seq.collect(fun r -> fetchStateData(r.Abbreviation))
|> Seq.length
I now want to run these async and I am running into a problem with AsyncLoad:
let fetchStateDataAsync(stateCode:string)=
async{
let uri = System.String.Format("https://portalvhdspgzl51prtcpfj.blob.core.windows.net/censuschicken/{0}.TXT",stateCode)
let! stateData = censusDataContext.AsyncLoad(uri)
return stateData.Rows
}
let usaData = stateCodes.Rows
|> Seq.collect(fun r -> fetchStateDataAsync(r.Abbreviation))
|> Seq.length
The error message is
The type 'Async<seq<CsvProvider<...>.Row>>' is not compatible with the type 'seq<'a>'
Forgive my lack of async knowledge, but do I have to use something other than Seq.Collect when applying async functions?
Thanks in advance
The problem is that turning code to asynchronous (by wrapping it in the async { .. } block) changes the result from seq<Row> to Async<seq<Row>> - that is, you now get an asynchronous computation that will eventually complete and return the sequence.
To fix this, you need to somehow start the computation and wait for the result. There is a number of choices - like running one by one sequentially. Probably the easiest option (and maybe the best - depending on what you want to do) is to run the computations in parallel:
let getAll =
stateCodes.Rows
|> Seq.map(fun r -> fetchStateDataAsync(r.Abbreviation))
|> Async.Parallel
This gives you an asynchronous computation that runs all the downloads and returns an array of results. You can run this synchronously (and block) and get the results:
getAll |> Async.RunSynchronously
|> Seq.collect id
|> Seq.length
If you want to run the downloads asynchronously in the background you can do that to, but you need to specify what to do with the result. For example:
async {
let! all = getAll
all |> Seq.collect id |> Seq.length |> printfn "Length %d" }
|> Async.Start