Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add while! (while-bang) keyword to computation expressions #1038

Closed
5 tasks done
mvkara opened this issue Jun 28, 2021 · 1 comment
Closed
5 tasks done

Add while! (while-bang) keyword to computation expressions #1038

mvkara opened this issue Jun 28, 2021 · 1 comment

Comments

@mvkara
Copy link

mvkara commented Jun 28, 2021

Add while! (while-bang) keyword to computation expressions

I propose we extend the computation expression syntax with a new keyword, while!, that combines a let! and a typical while loop.

Using the current GRPC client as an example instead of writing something like...

task {
   let! firstRead = sender.ResponseStream.MoveNext(ct)
   let mutable read = firstRead
   while read do
     q.Push(sender.ResponseStream.Current)
     let! nextRead = sender.ResponseStream.MoveNext(ct)
     read <- nextRead
}

We should be able to do

task {
   while! sender.ResponseStream.MoveNext(ct) do
    q.Push(sender.ResponseStream.Current)
}

NOTE: That the typical approach for using recursion doesn't work for TaskBuilder's from my understanding since they aren't tail recursive in many libraries that provide them.

A similar problem exists when reading rows of an ADO.NET reader. If the while! syntax was allowed we would be able to do something like:

asyncSeq {
    // https://docs.microsoft.com/en-us/dotnet/api/system.data.common.dbdatareader.readasync?view=netcore-3.1
    while! reader.MoveNextAsync() do
        yield {| Column1 = reader.GetInt32(0); Column2 = reader.GetInt32(1) |}
}

Instead of:

asyncSeq {
   let rec yieldAsync() = asyncSeq {
      // https://docs.microsoft.com/en-us/dotnet/api/system.data.common.dbdatareader.readasync?view=netcore-3.1
      let! hasNext = reader.MoveNextAsync() |> Async.AwaitTask
      if hasNext
      then
          yield {| Column1 = reader.GetInt32(0); Column2 = reader.GetInt32(1) |}
          yield! yieldAsync()
   }
   yield! yieldAsync()
}

Pros and Cons

Pros: More concise code aiding readability, allows a more intuitive use of libraries exposing this pattern especially when given a Task API where using the recursive approach may not be supported, may offer a performance improvement for certain cases.

Cons: More work required to support this in the language and associated computational expressions (e.g. Task builders such as Ply).

Extra information

Estimated cost (XS, S, M, L, XL, XXL): S

Related suggestions:

#863

Affidavit (please submit!)

Please tick this by placing a cross in the box:

  • This is not a question (e.g. like one you might ask on stackoverflow) and I have searched stackoverflow for discussions of this issue
  • I have searched both open and closed suggestions on this site and believe this is not a duplicate
  • This is not something which has obviously "already been decided" in previous versions of F#. If you're questioning a fundamental design decision that has obviously already been taken (e.g. "Make F# untyped") then please don't submit it.

Please tick all that apply:

  • This is not a breaking change to the F# language design
  • I or my company would be willing to help implement and/or test this

For Readers

If you would like to see this issue implemented, please click the 👍 emoji on this issue. These counts are used to generally order the suggestions by engagement.

@dsyme
Copy link
Collaborator

dsyme commented Jun 29, 2021

I agree with this, I'll mark it approved-in-princple

This was referenced Jun 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants