Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

Naming notes #77

Closed
dsyme opened this issue Nov 5, 2022 · 4 comments
Closed

Naming notes #77

dsyme opened this issue Nov 5, 2022 · 4 comments
Assignees
Milestone

Comments

@dsyme
Copy link
Contributor

dsyme commented Nov 5, 2022

I do notice the name "TaskSeq" is confusing some people - people thinking this is about "sequences of tasks". I'm not sure what to do about this.

While thinking about this I included some general notes on naming in this space, see below

  • F# IEnumerator

    • cold start
    • run once
    • no implicit cancellation token
    • no asynchronous waits
    • many results
    • state machines
    • = HotSynchronousFastEnumerator
    • = IEnumerable<T>
  • F# IEnumerable = Seq

    • cold start
    • run multiple
    • no implicit cancellation token
    • no asynchronous waits
    • many results
    • state machines
    • = ColdSynchronousFastEnumerable
    • ~= unit -> IEnumerable<T>
  • .NET/F#/C# Task = C# async/await

    • hot start
    • run once
    • no implicit cancellation token
    • asynchronous waits
    • one result
    • state machines
    • = HotAsynchronousFastValue
    • = Task<T>
  • IcedTask ColdTask

    • cold start
    • run many times
    • no implicit cancellation token
    • asynchronous waits
    • one result
    • state machines
    • = ColdAsynchronousFastValueFactory
    • ~= unit -> Task<T>
  • IcedTask CancellableTask

    • cold start
    • run many
    • implicit cancellation token
    • asynchronous waits
    • one result
    • state machines
    • = ColdAsynchronousFastCancellableValueFactory
    • ~= CancellationToken -> Task<T>
  • Async = F# async

    • cold start
    • run multiple
    • implicit cancellation token
    • asynchronous waits, one result
    • no state machines
    • = ColdAsynchronousCancellableValueFactory
    • ~= CancellationToken -> Task<T>
  • Current F# AsyncSeq

    • cold start
    • run multiple
    • implicit cancellation token
    • asynchronous waits
    • many results
    • no state machines
    • = ColdAsynchronousCancellableEnumerable
    • ~= CancellationToken -> IAsyncEnumerator<T>
  • Current F# TaskSeq

    • cold start
    • run multiple
    • implicit cancellation token governing iteration but not passed to each task along the way
    • asynchronous waits
    • many results
    • state machines
    • = ColdAsynchronousHalfCancellableEnumerable
    • ~= CancellationToken -> IAsyncEnumerator<T>

I'm leaving the question of tailcalls off the list, as much as I'd like to address that.

It's worth noting that at a high level there's no real logical difference between CancellableTask and F# Async<_>. Nor between F# TaskSeq and F# AsyncSeq.

The sweet spot for F# is really Cold+RunMany+Asynchronous+Fast+Cancellable+Tailcalls, which is what TaskSeq is close to being technically (except tailcalls, sadly).

@dsyme
Copy link
Contributor Author

dsyme commented Nov 5, 2022

Anyway, I just want to point out that the TaskSeq naming isn't perfect

Putting aside legacy, the ideal for naming and simplicity purposes would be

  • Make F# Async properly fast by getting it implemented using state machines, supporting tailcalls etc.
  • Call this library AsyncSeq and get it to support tailcalls (obviously deprecating the old AsyncSeq)

Then we'd be back to the nicest position that there is just async { .. } and asyncSeq { ... } and both support implicit cancellation token passing.

That makes me wonder if we should already be deprecating/renaming FSharp.Control.AsyncSeq to make room for this. Maybe renaming to FSharp.Control.AsyncSeqSlow or something. Or maybe this library should be even bolder and simply re-implement async as well, leaving us in the nicest spot of all (apart from the really tricky question of tailcalls, which I think needs a language addition to help builders know when in tailcall position).

@abelbraaksma
Copy link
Member

I see your point, but I'm not sold on this. F# Core has two distinct builders, one for task and one for async. The hot- vs cold-started, and resumable vs non-resumable, on top of the current discrepancy on tail-call recursion (async) and non tail-call recursion (task), at least for the foreseeable future allows for distinct use cases.

People have come to associate Async naming with multi-threading and parallelization, while Task naming is mostly associated with fast, hot-started, non-parallelized, yet asynchronous scenarios.

If we want to move forward to a single library approach for asynchronous sequences, it's likely that some of the current library functions of AsyncSeq will need to be dropped (I'd have to investigate, but I wouldn't be surprised if some functionality is currently not (easily?) possible with the resumable state machine approach).

Also, since most F# programmers that I know have typically moved from async builders to task builders "everywhere", it seems to make sense to do the same here. I.e., in a single library approach, the main building block would be TaskSeq, not AsyncSeq, in analogy with F# tasks.

I realize there's no perfect solution here. I can certainly see a use-case, and definitely until TaskSeq has gotten enough maturity, that AsyncSeq should continue to exist.

Also, with 250k downloads of the latest version alone, and 1.4M in total, deprecating AsyncSeq is sure going to annoy some people out there...

@abelbraaksma
Copy link
Member

abelbraaksma commented Nov 6, 2022

people thinking this is about "sequences of tasks". I'm not sure what to do about this.

PS: yes, I can see this confusion. The reason I started all of this was that in my team people were literally writing code that had List<Task<'T>>, which, when hot-started and with side-effects, is a recipe for disaster.

This prompted me, among giving some education on task (people really don't easily get hot-start vs delay/cold-start), to create TaskSeq based on your code, to force people using this pattern instead.

Which reminds me, I should probably remove functions like TaskSeq.ofTaskList, as they allow people to use bad practices (I added them in as one could have a list of cold tasks, but it's too easy to confuse them, unless we expand this lib into a single lib that contains all of IcedTasks as well so we can turn this into a TaskSeq.ofColdTaskList or something, without adding a dependency)...

@abelbraaksma
Copy link
Member

abelbraaksma commented Nov 6, 2022

Current F# AsyncSeq

Noticed something else: AsyncSeq does not implement IAsyncEnumerable<'T> from the BCL. Probably because it simply didn't exist at the time:

let x = asyncSeq {
    yield 1
}
x :> IAsyncEnumerable<_> // not possible

We could also implement that interface, though it'll be confusing, as the internally used interface is same-named, they only differ in the naming of getting the enumerator: GetAsyncEnumerator(CancellationToken) vs GetEnumerator().

I know there's AsyncSeq.toAsyncEnum and AsyncSeq.ofAsyncEnum, which were introduced for interoperating, but altogether it furthers my believe that it's probably better to continue on the path of using a different name altogether. Having two AsyncSeqs in the wild that implement different interfaces may just add to the confusion.

Not saying there isn't any confusion now. But I just think that having the two disparate yet seemingly equal worlds: tasks and asyncs in the F# ecosystem maybe the best of all the bad choices we have ;).

@fsprojects fsprojects locked and limited conversation to collaborators Oct 31, 2023
@abelbraaksma abelbraaksma converted this issue into discussion #188 Oct 31, 2023
@abelbraaksma abelbraaksma added this to the v0.1.0 milestone Mar 18, 2024
@abelbraaksma abelbraaksma pinned this issue Mar 18, 2024
@abelbraaksma abelbraaksma self-assigned this Mar 18, 2024

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Projects
None yet
Development

No branches or pull requests

2 participants