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

Feat: Implement builder for valuetask #14755

Closed

Conversation

thinkbeforecoding
Copy link
Contributor

This is a proposition of implementation of a valuetask computation expression.

A explained in fsharp/fslang-suggestions#1244 :

The advantages of making this adjustment to F# are:

More efficient and faster code
Far lower memory impact
No need for wrapping Task in ValueTask (which is not easy to get right and is verbose)
More straightforward in case of libraries and frameworks that use ValueTask
The disadvantages of making this adjustment to F# are :

A new ValueTaskBuilder implementation quite similar to TaskBuilder.

This implementation rely on .IsCompleted to avoid accessing the underlying Task when not needed.

As noticed in tests, an helper function could be useful to make a ValueTask from a ValueTask<unit>. Since `ValueTask<'t>' is a struct, it cannot be cast. It could be implemented as:

let toValueTask (task: ValueTask<unit>) : ValueTask =
        if task.IsCompleted then
            ValueTask.CompletedTask
        else
            task.AsTask() |> ValueTask   

But I don't know yet what would be the best name, and where to put it.

@thinkbeforecoding thinkbeforecoding requested a review from a team as a code owner February 15, 2023 15:39
Copy link
Member

@vzarytovskii vzarytovskii left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good.

Proposal need to be approved first, before we can merge it though.

It will probably also need an RFC which will describe CE, and which methods it will have (especially, around what can be bound inside - do we support full spectrum of awaitables, or just tasks/vtasks, how conversions are handled, etc), for documentation purposes.

@thinkbeforecoding
Copy link
Contributor Author

sure,
for now it can bind on ValueTask, Task, Async and awaitables. (I put Task in medium priority with Async to avoid resolution problems with failwith)

@thinkbeforecoding
Copy link
Contributor Author

btw, who is in charge of writing the RFC ?

@thinkbeforecoding
Copy link
Contributor Author

I don't know what triggers this error of duplicate compensation@17 .. Seems related to the code generation in .TryFinally, but I can't see why it's failing only in specific builds...

@vzarytovskii
Copy link
Member

btw, who is in charge of writing the RFC ?

It can be up to anyone, in most of the cases it's whoewer imlements the feature. The flow is usually Suggestion -> Approval -> RFC (+iterations) -> Implementation. Last two can be in parallel, to not waste time.

@T-Gro
Copy link
Member

T-Gro commented Feb 16, 2023

To simplify the review process, can you please explain how the implementation was created ?
Did you take the taskbuilder and adjust all pieces to work with ValueTask, or are there any outstanding changes compared to the existing taskbuilder?

@thinkbeforecoding
Copy link
Contributor Author

thinkbeforecoding commented Feb 17, 2023

I tried to make as few changes as possible compared to TaskBuilder:

  • I renamed types in the file to add a Value prefix
  • used AsyncValueTaskMethodBuilder from the Bcl instead of AsyncTaskMethodBuiler (this class is available only in netstandard2.1, the reason for the #if)
  • Changed the argument and return types to ValueTask
  • removed implementations related to backgroundtask

Then where new tasks are created, I used the IsCompleted property to return synchronously, and fallback to task creation if not completed.

I also added a Bind for task, added to the Medium Priority to avoid conflict in cases of return failwith "..."

Last part is adding all the test by copying the tests from task, and adapting to valuetask .

@vzarytovskii
Copy link
Member

vzarytovskii commented Feb 17, 2023

In general, I don't think it is a good idea to split codegen for FSharp.Core that much in case of ns21 as long as we still target ns20. It is very confusing to the users.

Might be a better idea to have it as separate package, once we start separating FSharp.Core cc @KevinRansom

@vzarytovskii vzarytovskii marked this pull request as draft February 17, 2023 15:26
@thinkbeforecoding
Copy link
Contributor Author

Actually, ValueTask is available only in netstandard2.1, and is used more and more in recent frameworks.
Having it OOTB in F# seemed sensisble.

@vzarytovskii
Copy link
Member

vzarytovskii commented Feb 17, 2023

Actually, ValueTask is available only in netstandard2.1, and is used more and more in recent frameworks. Having it OOTB in F# seemed sensisble.

What I meant is - it is confusing for users to have the whole builder api in FSharp.Core - netstandard2.1, and not have it in netstandard2.0.

We never did that big of the surface area difference yet (biggest one so far is TryFinally in Task available only in ns21, if I'm not mistaken).

@thinkbeforecoding
Copy link
Contributor Author

Yes I understand. But at some point some new features will appear only in net6. 0+... Having them in Fsharp.Core will have to follow.

@vzarytovskii
Copy link
Member

Yes I understand. But at some point some new features will appear only in net6. 0+... Having them in Fsharp.Core will have to follow.

I wonder if there's a better way for us to deliver those. Maybe layer corelib as FSharp.Core and all .Control. packages separately, and give those the same treatment we have for FSharp.Core currently.

@thinkbeforecoding
Copy link
Contributor Author

Yes that could be a nice way to do it.

Still, since the type ValueTask<'t> itself is only available in netstandard2.1 and compatible frameworks (Core 2.1, Core 2.2, Core 3.0, Core 3.1, 5, 6, 7), I'm not sure anyone using netstandard2.0 or less will find it strange that the CE doesn't exist. It make just no sense on this framework.

Like I would not expect task to exist on super-old frameworks where Task did not exist.

https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.valuetask?view=net-7.0

@TheAngryByrd
Copy link
Contributor

TheAngryByrd commented Feb 21, 2023

Also take a look over and use anything from IcedTasks ValueTask and the corresponding tests

@Daniel-Svensson
Copy link
Contributor

What I meant is - it is confusing for users to have the whole builder api in FSharp.Core - netstandard2.1, and not have it in netstandard2.0.

Why not netstandard2.0? The type is available from ns1.0 using System.Threading.Tasks.Extensions nuget package so ns2.0 or net461 should pose no problem.

@vzarytovskii
Copy link
Member

What I meant is - it is confusing for users to have the whole builder api in FSharp.Core - netstandard2.1, and not have it in netstandard2.0.

Why not netstandard2.0? The type is available from ns1.0 using System.Threading.Tasks.Extensions nuget package so ns2.0 or net461 should pose no problem.

Core library doesn't have any external dependencies, nor do we want it to have any at this point without proper design (to either properly separate it to multiple packages, or bump minimal version, or split codegen, or some secret 4th option).

@vzarytovskii
Copy link
Member

Going to close it for now, until we have a better story of how to deliver layered core library for basic ns2.0 functionality and newer stuff (fsharp/fslang-suggestions#1244 (comment))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

5 participants