Skip to content
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

More arities for tuple returning zip extension method #43702

Closed
Logerfo opened this issue Oct 21, 2020 · 10 comments · Fixed by #47147
Closed

More arities for tuple returning zip extension method #43702

Logerfo opened this issue Oct 21, 2020 · 10 comments · Fixed by #47147
Assignees
Labels
api-approved API was approved in API review, it can be implemented area-System.Linq help wanted [up-for-grabs] Good issue for external contributors
Milestone

Comments

@Logerfo
Copy link

Logerfo commented Oct 21, 2020

Background and Motivation

#43687 (comment)

Proposed API

namespace System.Linq
{
    public static class Enumerable
    {
+       public static IEnumerable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, IEnumerable<TThird> third);
    }

    public static class Queryable
    {
+       public static IQueryable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this IQueryable<TFirst> source1, IEnumerable<TSecond> source2, IEnumerable<TThird> source3);
    }
}

Usage Examples

foreach(var (a, b, c) in first.Zip(second, third))
{
   //...
}

Alternative Designs

public static IEnumerator<(T1, T2, T3)> GetEnumerator<T1, T2, T3>(this (IEnumerable<T1> a, IEnumerable<T2> b, IEnumerable<T3> c) pair)
    => pair.a.Zip(pair.b, (a, b) => (a, b)).Zip(pair.c, (a, c) => (a.a, a.b, c)).GetEnumerator();
foreach(var (a, b, c) in (collectionA, collectionB, collectionC)) 
{
	// This block will execute only while both `a`, `b` and `c` continue to yield values

	// Code goes here
}
@Logerfo Logerfo added the api-suggestion Early API idea and discussion, it is NOT ready for implementation label Oct 21, 2020
@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added area-System.Linq untriaged New issue has not been triaged by the area owner labels Oct 21, 2020
@ghost
Copy link

ghost commented Oct 21, 2020

Tagging subscribers to this area: @eiriktsarpalis, @jeffhandley
See info in area-owners.md if you want to be subscribed.

@eiriktsarpalis eiriktsarpalis added api-ready-for-review API is ready for review, it is NOT ready for implementation and removed api-suggestion Early API idea and discussion, it is NOT ready for implementation untriaged New issue has not been triaged by the area owner labels Dec 2, 2020
@eiriktsarpalis eiriktsarpalis self-assigned this Dec 2, 2020
@eiriktsarpalis
Copy link
Member

Proposal looks good to me. To be determined: what is the maximum arity we are willing to support?

@eiriktsarpalis
Copy link
Member

eiriktsarpalis commented Dec 2, 2020

Note that we would also need to include the reducing selecting overloads:

namespace System.Linq
{
    public static class Enumerable
    {
        public static IEnumerable<TResult> Zip<TFirst, TSecond, TThird, TResult>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, IEnumerable<TThird> third, Func<TFirst, TSecond, TThird, TResult> resultSelector);
        public static IEnumerable<TResult> Zip<TFirst, TSecond, TThird, TFourth, TResult>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, IEnumerable<TThird> third, IEnumerable<TFourth> fourth, Func<TFirst, TSecond, TThird, TFourth, TResult> resultSelector);
        ...
    }
}

as well as the IQueryable variants:

namespace System.Linq
{
    public static class Queryable
    {
        public static IQueryable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this IQueryable<TFirst> source1, IEnumerable<TSecond> source2, IEnumerable<TThird> source3);
        public static IQueryable<(TFirst First, TSecond Second, TThird Third, TFourth Fourth)> Zip<TFirst, TSecond, TThird, TFourth>(this IQueryable<TFirst> source1, IEnumerable<TSecond> source2, IEnumerable<TThird> source3, IEnumerable<TFourth> source4);
        ...

        public static IQueryable<TResult> Zip<TFirst, TSecond, TThird, TResult>(this IQueryable<TFirst> first, IEnumerable<TSecond> second, IEnumerable<TThird> third, Expression<Func<TFirst, TSecond, TThird, TResult>> resultSelector);
        public static IQueryable<TResult> Zip<TFirst, TSecond, TThird, TFourth, TResult>(this IQueryable<TFirst> first, IEnumerable<TSecond> second, IEnumerable<TThird> third, IEnumerable<TFourth> fourth, Expression<Func<TFirst, TSecond, TThird, TFourth, TResult>> resultSelector);
        ...
    }
}

@Logerfo would you be able to update your proposal?

@Logerfo
Copy link
Author

Logerfo commented Dec 2, 2020

@eiriktsarpalis
I think that a good starting point on the maximum arity is the System.Tuple arities. It goes up to 7 type arguments until it starts nesting the tuples.
I have updated the proposal to contain the method groups you pointed out.
Thanks.

@svick
Copy link
Contributor

svick commented Dec 27, 2020

I'm not against this proposal, but I'm not sure how much value it adds, since you can do the following today:

foreach (var ((a, b), c) in first.Zip(second).Zip(third))

@Logerfo
Copy link
Author

Logerfo commented Dec 29, 2020

@svick it adds value on legibility and succinctness and it grows with the arity:

foreach (var ((((((a, b), c), d), e), f), g) in first.Zip(second).Zip(third).Zip(fourth).Zip(fifth).Zip(sixth).Zip(seventh))
foreach (var (a, b, c, d, e, f, g) in first.Zip(second, third, fourth, fifth, sixth, seventh))

@terrajobst
Copy link
Member

terrajobst commented Jan 5, 2021

Video

  • Let's support only three for now as four seems fairly rare.
  • We decided to not add an overload for the one taking the result selector.
namespace System.Linq
{
    public static class Enumerable
    {
        public static IEnumerable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, IEnumerable<TThird> third);
    }

    public static class Queryable
    {
        public static IQueryable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this IQueryable<TFirst> source1, IEnumerable<TSecond> source2, IEnumerable<TThird> source3);
    }
}

@terrajobst terrajobst added api-approved API was approved in API review, it can be implemented and removed api-ready-for-review API is ready for review, it is NOT ready for implementation labels Jan 5, 2021
@eiriktsarpalis eiriktsarpalis added the help wanted [up-for-grabs] Good issue for external contributors label Jan 5, 2021
@eiriktsarpalis eiriktsarpalis added this to the 6.0.0 milestone Jan 5, 2021
@GSPP
Copy link

GSPP commented Jan 6, 2021

I think that's a great decision. Zip itself is a fairly rare operation and zipping many sequences becomes rarer as the number grows.

I also find the workaround var ((((((a, b), c), d), e), f), g) to be rather clear. That seems very practical.

@eiriktsarpalis
Copy link
Member

@terrajobst your post still contains the resultSelector overloads. I've removed them to reflect the api review decision.

@Logerfo
Copy link
Author

Logerfo commented Jan 6, 2021

OP updated.

@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Jan 19, 2021
@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Jan 19, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Feb 18, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-approved API was approved in API review, it can be implemented area-System.Linq help wanted [up-for-grabs] Good issue for external contributors
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants