Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Adding ITuple interface and implementation in ValueTuple #10068

Closed
wants to merge 4 commits into from

Conversation

jcouv
Copy link
Member

@jcouv jcouv commented Jul 14, 2016

Re-opening the discussion following C# language design meeting yesterday.

The purpose of this interface is to allow objects to be used in positional pattern matching.
ValueTuple is the first type to implement it.

From discussion with LDM yesterday, it seems that existing collection interfaces (IReadOnlyCollection and IReadOnlyList) don't fit the purpose. We feel there should be no iterator involved. Also we need an interface as a marker to identify types that are suitable for such usage.
We are still having some debate on the proper name for this interface (ITuple, IDeconstructable, IIndexable, ...).
I renamed Size to be Length to avoid introducing a new term.

@jcouv
Copy link
Member Author

jcouv commented Jul 14, 2016

@stephentoub Let me know how we should proceed. I'm happy to schedule a discussion if you feel it would help.

/// <summary>
/// The number of positions in this data structure.
/// </summary>
public int Length => 0;
Copy link
Member

@gafter gafter Jul 14, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these should be explicit implementations. I don't think we want to prevent Length from being a valid tuple "friendly" element name. #Resolved

Copy link
Member Author

@jcouv jcouv Jul 14, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'll do that. #Resolved

Copy link
Member Author

@jcouv jcouv Jul 14, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done #Resolved

@gafter
Copy link
Member

gafter commented Jul 14, 2016

:shipit:

{
get
{
return 7 + ((ITuple)Rest).Length;
Copy link
Contributor

@justinvp justinvp Jul 15, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Casting to ITuple is going to box Rest. The box allocation can be avoided by removing the cast (which works without boxing because of the constraint):

return 7 + Rest.Length;

@stephentoub
Copy link
Member

Other than Justin's comments, LGTM.

@terrajobst, anything we need to do here from an API review perspective?

@jcouv
Copy link
Member Author

jcouv commented Jul 19, 2016

@terrajobst, are we good to move forward with this PR?

@@ -920,7 +920,7 @@ public static class TupleExtensions
}
#endregion

private static ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest> CreateLong<T1, T2, T3, T4, T5, T6, T7, TRest>(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, TRest rest) where TRest : struct =>
private static ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest> CreateLong<T1, T2, T3, T4, T5, T6, T7, TRest>(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, TRest rest) where TRest : struct, ITuple =>
new ValueTuple<T1, T2, T3, T4, T5, T6, T7, TRest>(item1, item2, item3, item4, item5, item6, item7, rest);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need the constraint to struct?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TRest must be a nested ValueTuple.
Unfortunately, we don't have a good way to express that constraint, so I am using a weaker (but correct) constraint. ValueTuple is a struct.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why ITuple is not enough of a constraint?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ITuple is public (other types can implement it). It applies to any type that can be used in dynamic pattern matching (when you want to break an object into its elements at runtime).
Also ITuple says nothing about class vs struct, but ValueTuple must be a struct to be usable by the compiler (this will be implemented shortly, dotnet/roslyn#11689).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We seem to completed a cycle: I asked why it has to be a struct, and now you said that [because] "ValueTuple must be a struct". So, again, why does it have to be a struct?

Copy link
Member

@KrzysztofCwalina KrzysztofCwalina Jul 28, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I love the fact that now tuples (ValueTyple*) are structs. I just think that constraining TRest to struct is unnecessary. If TRest is generated by the compiler, you will make it a struct, i.e. you don't need the constraint.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a strong opinion on the struct constraint, but note that removing it will likely force us to add a bit more code, e.g. to protect against Rest being null here:
https://github.com/dotnet/corefx/blob/master/src/System.ValueTuple/src/System/ValueTuple/ValueTuple.cs#L2042
Since we really want this to be constrained to be a value tuple, all of which are structs, and we don't expect nor want anything else in this position, why remove the constraint? What harm does it cause to have it (i.e. what scenario are you envisioning)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First, I was simply asking why we have the constraint, not trying to push to remove it. And so I gather the answer is that we don't want to do null checks in ToString?

But secondly, I wonder if it would not be better for rest to be a class after a certain arity. I think past certain arity, the struct will get so large that it would be cheaper to allocate on the heap than to make the copies.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not the reason; I was simply pointing out a side effect of removing it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me, the reason is that we want to constrain to a ValueTuple, but we can't express that, so we apply the maximum constrain that we can express.

I find the idea of switching to nesting class (most likely System.Tuple) after a certain arity intriguing. But that means the compiler would have to be aware of that type too. Even if we removed the struct constraint to leave room for such an option in the future, we'd face the problem that ValueTuple and System.Tuple don't implement the same internal interface.

@jcouv
Copy link
Member Author

jcouv commented Aug 2, 2016

From corefx review and discussion, ITuple will not be implemented before NetStandard 2.0, so that we limit runtime inconsistencies between System.ValueTuple and System.Tuple. We'll only ship ITuple on ValueTuple when it can also be added to System.Tuple.

Closing this PR.

@jcouv
Copy link
Member Author

jcouv commented Nov 18, 2016

@gafter This is the PR I had punted. I will use this change as template for the various mscorlibs.

@karelz karelz modified the milestone: 1.1.0 Dec 3, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants