fsharp icon indicating copy to clipboard operation
fsharp copied to clipboard

Feat: Implement builder for valuetask

Open thinkbeforecoding opened this issue 2 years ago • 15 comments

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

A explained in https://github.com/fsharp/fslang-suggestions/issues/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 avatar Feb 15 '23 15:02 thinkbeforecoding

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 avatar Feb 15 '23 15:02 thinkbeforecoding

btw, who is in charge of writing the RFC ?

thinkbeforecoding avatar Feb 15 '23 16:02 thinkbeforecoding

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...

thinkbeforecoding avatar Feb 15 '23 16:02 thinkbeforecoding

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.

vzarytovskii avatar Feb 15 '23 16:02 vzarytovskii

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?

T-Gro avatar Feb 16 '23 19:02 T-Gro

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 .

thinkbeforecoding avatar Feb 17 '23 14:02 thinkbeforecoding

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 avatar Feb 17 '23 15:02 vzarytovskii

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

thinkbeforecoding avatar Feb 17 '23 15:02 thinkbeforecoding

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).

vzarytovskii avatar Feb 17 '23 15:02 vzarytovskii

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.

thinkbeforecoding avatar Feb 17 '23 20:02 thinkbeforecoding

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.

vzarytovskii avatar Feb 17 '23 21:02 vzarytovskii

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

thinkbeforecoding avatar Feb 18 '23 16:02 thinkbeforecoding

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

TheAngryByrd avatar Feb 21 '23 00:02 TheAngryByrd

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.

Daniel-Svensson avatar Apr 21 '24 15:04 Daniel-Svensson

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 avatar Apr 21 '24 20:04 vzarytovskii

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 (https://github.com/fsharp/fslang-suggestions/issues/1244#issuecomment-2161839536)

vzarytovskii avatar Jun 12 '24 00:06 vzarytovskii