[Pitch] withLatestFrom

the enum part you mean ? in the end it is just about extracting the a sequence with pattern matching.

the hard part would be the return type of such function. we have to return a type that fits the 3 strategies and it might not scale well, or we would have to have a wrapper type but I'm not sure this is the good way. I guess we should stick to 1 function per usage.

Like this (not super nice naming but for illustration purpose)

zipLatestFromAll(a, b) -> AsyncZip2LatestFromAllSequence
zipLatestFromAny(a, b) -> AsyncZip2LatestFromAnySequence
zipLatestFromOne(a, latest: b) -> AsyncZip2LatestFromOneSequence
1 Like

Speaking solely about the API, without taking implementation details in consideration, I prefer this:

IMO, the following adjustments would be even better:

zip(a, b, c, nextElementOn: .newElementsFromAll)
zip(a, b, c, nextElementOn: .newElementFromAny)
zip(a, b, nextElementOn: .newElementFrom(c))

Since, @Tony_Parker, proposed the unified API with strategy flag approach. I would love to hear feedback on the implementation complexity and challenges on documentation for a function with many different use cases. I like this approach, but there are some challenges, indeed.

I also don't think the API below is as clear as the former.

1 Like

I can also foresee a semantic issue with the API below.

It is not clear if c is going to be included in the zipped output tuple or if it is just going to be used to set the sampling rate.

Thinking about the implementation complexity, specially about the output type. I think that that can be resolved with overloads. While reading the function, it would seem like that there is only one signature, but in reality we would have different signatures for the first two cases:

And a different signature for just:

Because of the extra generic type for the strategy. We could solve that with static properties on structs, instead of enums? Like some SwiftUI APIs.

Using different functions instead of a strategy argument is fine (maybe it's helpful to have the different return types). Similarity in naming would be enough.

The point made about withLatestFrom referring to self is interesting. That suggests an extension on AsyncSequence vs zip and combineLatest being free functions. Keeping in mind we do want to support N arguments when we have variadic generics:

func zip(a, b, ...) // 'and'
func zipLatest(a, b, ...) // 'or'
extension AsyncSequence {
    func zipWhen(other1, other2, ...) // this name seems backwards. any other suggestions?
}

Ok, I think it is beginning to take shape. If I try to sum up:

  • there is an interest in such an operator since it is a complement to the already implemented zip / combineLatest
  • unlike zip and combineLatest, this operator is closely related to self, so it makes sense to have an extension function, rather than a high level function
  • the name should also contain zip in order to be included in the zip family operators, so it facilitates the ramp up for developers

Like zip and combineLatest, I guess we will stick to 2 versions (zipWhen(other), zipWhen(other1, other2)) since it returns tuples.

@Tony_Parker @Philippe_Hausler do you agree with those statements so far ?

Topologically speaking that seems like a missing gap in the matrix of potential behaviors. So the first point definitely makes sense to me (this has been a requested feature for Combine a number of times too since it does not really have this particular behavioral shape).

I also agree that it is an algorithm targeting self, i.e. it should likely be a function as an extension (likely conditionally on Sendable conformances) on AsyncSequence. Effectively this feels similar to the concept of append(contentsOf:) where there is distinctly a "primary" subject of application, and in Swift we spell that as a member function.

The last point is interesting, not that I have any better suggestions. combineLatest to me feels quite natural language - it does what it says on the tin. However combineLatest still is in the subgroup family of zip in the regards it takes multiple inputs and produces a singular output that is a tuple of the elements of the inputs. Of course that is also in the larger group of things that take multiple inputs and produce a singular output. Whatever this is to be named, it fits in both the larger group of multiple inputs and a singular output, but also the subgroup of a multiple inputs with a singular output that is a tuple comprised of the elements of the input elements.

Perhaps what might be helpful to consider is; what would the type ferrying these values be called if there was no member function? I am not sure if AsyncZipWhenSequence offers any additional clarity to what the member function name should be.

Thanks for your feedback @Philippe_Hausler.

What would be the process if I wanted to move forward with the implementation ? Would it be a PR? a proposal?

Thanks.

A PR is quite welcomed, just understand that we currently have a back-log of things that need to be reviewed before adding new stuff (unless it is super compelling; for example if it has an existing analog that was missed in parity with synchronous versions in the standard library or in swift algorithms).

Proposals are also great to write up and flesh out the naming concepts, behavioral structure etc.

I think one thing that might be generally useful is to build up the matrix of behaviors with zip, combineLatest and this; showing a hole in the table of behaviors that this resolves gives a lot clout to its importance.

Hi @Philippe_Hausler

Just to let you know that I have submitted a PR https://github.com/apple/swift-async-algorithms/pull/147.

As I am new to the "evolution proposal" process, I hope everything is right.

Thanks.

Thanks, that definitely does a lot for getting it ready.

I want to make sure we run through the zip algorithm itself first before we get to the cohorts in that family of functions. My plan was to run zip for a few days gathering feedback so that it can be reviewed, and then move to the next most impactful (my current plan is to do combineLatest after that).

Doing reviews in that order should give us a good foundation to review this one but still give folks a good chance to offer the right level of feedback/interaction we need for the sibling methods in the family.

1 Like

Quick reviewing this thread: I definitely agree that there is space for a withLatestFrom(_:) equivalent in AsyncAlgorithms.

A couple of points:

  • I disagree with the grouping under zip. Zip is a particularly illustrative name that implies a strict ordering via the visual metaphor of a physical zip. This breaks down with zipWhen or zipLatestFrom. The closest visual metaphor here would be a broken zip. I would suggest that the grouping should be under combine, and the variations should be some spin on, combineLatest(_:when:) or combineLatest(_:on:) where 'when' or 'on' is some definition of the combination trigger. i.e. either left, or right, or the triggering sequence itself.
  • I understand that there is a distinction being made on whether an algorithm should be a member on self or a free function based upon whether it can be applied to self. But I do wonder if this is following the implementation detail rather than the most expressive syntax. If I'm deciding on whether to combine some Async Sequences using combineLatest(_:) zip(_:_) merge(_,_) etc, I think it may be easier to think about if straight line sections are 'chained' (member on self), map, joined etc. whereas manifold points are free functions combineLatest, zip etc.
1 Like