Comments (9)
Just to say this work is now ready for review, I'd be really grateful if people experienced with using TaskBuilder.fs could read the RFCs and review the implementation dotnet/fsharp#6811
For example, should FSharp.Core come with things like Task.Ignore
or other such APIs? Right now it's task { ... }
and backgroundTask { ... }
from taskbuilder.fs.
Also to mention that I removed the use of complex SRTP successfully replaced by a set of method overloads, see https://github.com/dotnet/fsharp/blob/feature/tasks/src/fsharp/FSharp.Core/tasks.fsi
Also see the RFC section https://github.com/fsharp/fslang-design/blob/master/RFCs/FS-1097-task-builder.md#bind-returnfrom-support
@rspeele Do you recall if there was a specific reason to use the more complex form of SRTP, with multi-parameter witnesses, priorities etc.? Beyond the need to resolve method overloading order?
from taskbuilder.fs.
Hey, I think how you have it now, using extension methods in modules opened highest-priority-last, is how TaskBuilder used to work back when I last significantly contributed to it. Something like this version.
TaskBuilder in this form was, unbeknownst to me, dependent on a subtle change to type inference introduced in F# 4.1. When others tried to use it with the F# 4.0 compiler they got type inference errors and ambiguity over which Bind or and ReturnFrom should be selected. @gusty swooped in to the rescue (thank you!) and added the SRTP magic to make it work as it does now, which plays nicely with both old and new versions of the compiler.
For purposes of a new addition to F#, compatibility with the old compiler might not be so relevant. As long as the type inference behavior stays consistent and keeps this simple module-priority extension method technique working, I personally think it's simpler and easier to understand.
This old issue and the ones linked from it is a good reference.
from taskbuilder.fs.
@dsyme Nice job !
I like explicit things, like Task.Ignore
and I think would be great to be explicit when converting to/from async, as it is more aligned with the F# way of doing things, and less with C# ad-hoc overloads.
from taskbuilder.fs.
@rspeele Thank you so much for the information
@gusty What would be your design principles for a Task module/type in F#? Should it follow the naming and design of Async? Or just be a couple of methods like Ignore?
from taskbuilder.fs.
@dsyme that's a good question.
I understand the value of aligning with Async, but at the same time I think Async type is a bit special in the sense that all methods are Pascal case, because of the overloads, which require method instead of functions.
My personal preference is not to follow that design and align instead with the rest of the FSharp.Core modules. When there are cases which requires overloads we can define different function names. An alternative to that would be use overloads but with camel case, you'll be breaking the naming conventions, but actually this reveals a pain point in the naming conventions in that the casing expose what I consider implementation details: module function or type member.
And as I said before, I'm not a big fan of ad-hoc overloading as their reasoning/understanding relies more on tooling and there are situations where they don't compose well.
I saw your job with Async2 maybe that will be a good opportunity to align the Async module.
Here's F#+ Task module, it's really a tiny set of functions, not saying that it's complete, more functions can be added: https://github.com/fsprojects/FSharpPlus/blob/master/src/FSharpPlus/Extensions/Task.fs
Finally I insist in the importance of being explicit when switching from Async and Task, for different task-like awaitables it is ok as we can consider them a set of the same abstraction.
from taskbuilder.fs.
It's certainly an important design point to consider.
We never wrote out a rationale for PascalCasing of Async. Here are my recollections for the record
-
Overloading was needed for FromBeginEnd and friends
-
Many of the constructs related to things that were present in .NET and had .NET naming (AwaitWaitHandle).
-
At the time, F# Async was seen as a compositional multi-threaded, "here be dragons" imperative programming mechanism that we wanted held at arms length from the "core" of data-oriented functional programming. (IEnumerable/Seq can also be be seen as an imperative programming mechanism but most commonly was used as a form of functional collection programming, and C# positioned it as such with LINQ).
-
We wanted the majority of async code to be written using
async { ... }
for clarity and debuggability so there was no desire to add Async.map etc. -
There was an element of the historical: there was general pressure at the time around F#'s
lowerCase
naming violating .NET design conventions. These included formal requirements to "review" and Microsoft-signed .NET DLLs with the high-priests at Microsoft, whose approval was needed. This led to CompiledName, unfortunate in retrospect though not catastrophically so.
Of these points 1, 2, 3 and 4 still hold to some extent and also apply to task.
We did add IEvent.map
and IObservable.map
and so on, which is inconsistent with some of the points above, especially (3). However there were existing "sources" of events and observables and the programming we expected for these was intended to be very transient/ephemeral - unlike async which we saw as a replacement for multi-threaded programming.
It would be lovely if we could somehow move all of this out of FSharp.Core, or split/refactor FSharp.Core.
I notice FSharpPlus Async.fs as an alternative viewpoint, though there's not a lot there.
I'd imagine we won't act on this as part of RFC FS-1097, but we should open a language suggestion for future Task/Async modules or classes.
from taskbuilder.fs.
Finally I insist in the importance of being explicit when switching from Async and Task, for different task-like awaitables it is ok as we can consider them a set of the same abstraction.
For me I think it's pretty much ok to implicitly bind an Async in a task. It's essentially foregoing the multi-start and cancellation token passing of the Async
The other way around should be explicit with AwaitTask since the cancellation token is lost
from taskbuilder.fs.
@dsyme thanks for sharing your thoughts, very interesting.
We should certainly open a suggestion for re-organizing F# core.
In F#+ Async module has only some most-wanted functions, it is by no means a complete module at the moment. There are also Async extensions, which follows the Pascal case naming because they are methods. But I'm not convinced that's the best way to go.
Even if we manage to move async to a module and use camel case names, we should review F# design guidelines, as there it states that methods should be Pascal case, as I said sometimes it is just an implementation detail.
The other way around should be explicit with AwaitTask since the cancellation token is lost
That would be my preferred way. I am OK with overloads as long as they represent the same abstraction, but I consider Task-likes and Async two different abstractions as although they address the same problem, the models differ a lot.
Also think about this if task accepts implicitly async, the expectation is that the opposite should also be true and it's not. So, better to be consistent.
from taskbuilder.fs.
Related Issues (20)
- Please elaborate a bit on "Tail calls are not optimized" HOT 2
- Async overload bind HOT 8
- Issue with plain Task and do! HOT 1
- TaskBuilder update breaks my tasks HOT 30
- TaskResultBuilder ? HOT 3
- TaskBuilder 2 release HOT 5
- Could not load type 'BindI' from assembly 'TaskBuilder.fs HOT 3
- Caching bool tasks HOT 2
- add support of specifying TaskScheduler HOT 4
- The type 'Threading.Tasks.Task' is not compatible with the type 'Threading.Tasks.Task<'a>' HOT 7
- Using ValueTask instead of Task HOT 1
- Intended way to call async and ignore result HOT 2
- Easy way to get result synchronously? HOT 5
- Support for netstandard2.0 HOT 11
- Code is not sufficiently generic
- Question on ContextInsensitive tasks HOT 41
- Non-generic Tasks HOT 6
- What is the purpose of this if it exists in FSharpx.Extras ? HOT 3
- Dispose is not called in task CE HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from taskbuilder.fs.