Giter VIP home page Giter VIP logo

Comments (9)

dsyme avatar dsyme commented on June 12, 2024

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.

dsyme avatar dsyme commented on June 12, 2024

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.

rspeele avatar rspeele commented on June 12, 2024

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.

gusty avatar gusty commented on June 12, 2024

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

dsyme avatar dsyme commented on June 12, 2024

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

gusty avatar gusty commented on June 12, 2024

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

dsyme avatar dsyme commented on June 12, 2024

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

  1. Overloading was needed for FromBeginEnd and friends

  2. Many of the constructs related to things that were present in .NET and had .NET naming (AwaitWaitHandle).

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

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

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

dsyme avatar dsyme commented on June 12, 2024

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.

gusty avatar gusty commented on June 12, 2024

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

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.