I'm trying to understand async workflows in F# but I found one part that I really don't understand.
The following code works fine:
let asynWorkflow = async{
    let! result = Stream.TryOpenAsync(partition) |> Async.AwaitTask 
    return result
    } 
let stream = Async.RunSynchronously asynWorkflow
             |> fun openResult -> if openResult.Found then openResult.Stream else Stream(partition)
I define a async workflow where TryOpenAsync returns a Task<StreamOpenResult> type. I convert it to Async<StreamOpenResult> with Async.AwaitTask. (Side quest: "Await"Task? It doesn't await it just convert it, does it? I think it has nothing to do with Task.Wait or the await keyword). I "await" it with let! and return it.
To start the workflow I use RunSynchronously which should start the workflow and return the result (bind it). On the result I check if the Stream is Found or not. 
But now to my first question. Why do I have to wrap the TryOpenAsync call in another async computation and let! ("await") it? E.g. the following code does not work:
let asynWorkflow =  Stream.TryOpenAsync(partition) |> Async.AwaitTask  
let stream = Async.RunSynchronously asynWorkflow
             |> fun openResult -> if openResult.Found then openResult.Stream else Stream(partition)
I thought the AwaitTask makes it an Async<T> and RunSynchronously should start it. Then use the result. What do I miss?
My second question is why is there any "Async.Let!" function available? Maybe because it does not work or better why doesn't it work with the following code?
let ``let!`` task = async{
    let! result = task |> Async.AwaitTask 
   return result
   } 
let stream = Async.RunSynchronously ( ``let!`` (Stream.TryOpenAsync(partition))  )
         |> fun openResult -> if openResult.Found then openResult.Stream else Stream(partition)
I just insert the TryOpenAsync as a parameter but it does not work. By saying does not work I mean the whole FSI will hang. So it has something to do with my async/"await".
--- Update:
Result of working code in FSI:
>
Real: 00:00:00.051, CPU: 00:00:00.031, GC gen0: 0, gen1: 0, gen2: 0
val asynWorkflow : Async<StreamOpenResult>
val stream : Stream
Result of not working code in FSI:
>
And you cannot execute anything in the FSI anymore
--- Update 2
I'm using Streamstone. Here the C# example: https://github.com/yevhen/Streamstone/blob/master/Source/Example/Scenarios/S04_Write_to_stream.cs
and here the Stream.TryOpenAsync: https://github.com/yevhen/Streamstone/blob/master/Source/Streamstone/Stream.Api.cs#L192
                        
I can't tell you why the second example doesn't work without knowing what
Streamandpartitionare and how they work.However, I want to take this opportunity to point out that the two examples are not strictly equivalent.
F#
asyncis kind of like a "recipe" for what to do. When you writeasync { ... }, the resulting computation is just sitting there, not actually doing anything. It's more like declaring a function than like issuing a command. Only when you "start" it by calling something likeAsync.RunSynchronouslyorAsync.Startdoes it actually run. A corollary is that you can start the same async workflow multiple times, and it's going to be a new workflow every time. Very similar to howIEnumerableworks.C#
Task, on the other hand, is more like a "reference" to an async computation that is already running. The computation starts as soon as you callStream.TryOpenAsync(partition), and it's impossible to obtain aTaskinstance before the task actually starts. You canawaitthe resultingTaskmultiple times, but eachawaitwill not result in a fresh attempt to open a stream. Only the firstawaitwill actually wait for the task's completion, and every subsequent one will just return you the same remembered result.In the async/reactive lingo, F#
asyncis what you call "cold", while C#Taskis referred to as "hot".