Replies: 14 comments
-
I think that this is a very dangerous assumption to make. The tuple elements are named, not arbitrary generic type arguments. The tuple type going in is not the same tuple type coming out. They are completely unrelated, and they should be completely unrelated. The names should only survive when the generic type argument is the tuple type itself, and the compiler already supports this: public static void Foo()
{
T Bar<T>(T value) => value;
var tuple = (a:1, b:2);
var newTuple = Bar(tuple); // T is (int a, int b)
var a = newTuple.a; // works fine
} |
Beta Was this translation helpful? Give feedback.
-
The issue with your code example is that T Bar<T, T1, T2>(T value) where T : (T1, T2) => value; but it's not possible to constrain on structs. I do not follow your reasoning that "The tuple type going in is not the same tuple type coming out. They are completely unrelated, and they should be completely unrelated." If I return the tuple passed in, then it clearly is the same tuple. The element names are pure sugar and extending that sugar to work in this scenario would be extremely useful. |
Beta Was this translation helpful? Give feedback.
-
That's an implementation detail. To the compiler the method accepts
In some cases, sure. But in all of the other cases where those tuples just happen to have the same types and number of elements those names are not only not useful, they're misleading. |
Beta Was this translation helpful? Give feedback.
-
Could you give some examples of where this would be misleading? I'm struggling to imagine a scenario where preserving element names would be less then helpful, let alone misleading. |
Beta Was this translation helpful? Give feedback.
-
I'd argue that it applies to pretty much every case where the method body doesn't just return the parameter. The fact that the method does this is something that the compiler wouldn't know. You're asking for the compiler to project names across an opaque boundary. You have another option here as well, you can declare the local function to return a tuple with element names rather than an unnamed tuple. public static void Foo()
{
(T1 a, T2 b) Bar<T1, T2>((T1, T2) value) => value;
var tuple = (a:1, b:2);
var newTuple = Bar(tuple);
var a = newTuple.a; // works fine
} |
Beta Was this translation helpful? Give feedback.
-
I have updated the OP to describe a more realistic scenario where the element names being projected through that boundary would be useful, as I'd argue the opposite: it pretty much would be useful in all cases. For clarity, I've reproduced the code here: public static TR Foo<T1, T2, TR>(this (T1, T2) t, Func<(T1, T2), TR> f) => f(t);
...
(a:1, b:1).Foo(t => t.a == t.b); // doesn't compile as the compiler doesn't "project"
// a & b through the (T1, T2) type declaration
That really would be confusing. If I passed in |
Beta Was this translation helpful? Give feedback.
-
I don't think that's any different. What happens between the invocation of that method and the invocation of the delegate are completely opaque. |
Beta Was this translation helpful? Give feedback.
-
Maybe a way to make those opaque types transparent to the compiler would be your "Support generics and generic type parameters in aliases" idea: using TupleType = (T1, T2);
...
public static TR Foo<T1, T2, TR>(this TupleType t, Func<TupleType, TR> f) => f(t);
...
(a:1, b:1).Foo(t => t.a == t.b); // All is now well as (a, b) is mapped to TupleType and
// so the element names can be safely projected That way, it's clear to the compiler that the two references to |
Beta Was this translation helpful? Give feedback.
-
Even in that case Do you actually have a use case where using tuples in this manner imposes a burden? I'd think that allowing the names to flow through a single generic type argument that represents the tuple type, along with its names, would be sufficient for most cases, e.g. LINQ. |
Beta Was this translation helpful? Give feedback.
-
Yes, I have a specific use-case. But that's specific. If the community (which seems to be you here, as no one else has commented), cannot see a use for the more generic example I've offered: using TupleType = (T1, T2);
...
public static TR Foo<T1, T2, TR>(this TupleType t, Func<TupleType, TR> f) => f(t);
...
(a:1, b:1).Foo(t => t.a == t.b); // this should work Then this proposal is dead in the water until others run into the same limitation and understand why this is a limitation. So I'll close this for now. |
Beta Was this translation helpful? Give feedback.
-
@DavidArno I've been reading and following the issue but I can't add anything useful besides saying that I agree with what @HaloFour said above:
Pretty much sums up my thoughts. :) |
Beta Was this translation helpful? Give feedback.
-
I get that. Where my head is at the moment, tuples are the single most important feature added to v7.0. They were enhanced by v7.1. But the inability of the language/compiler to grasp that tuple A is the same tuple as tuple B, when passed across an "opaque boundary" severely limits their use. But I accept that no one else gets that/accepts the need for that. So I've closed this issue. Maybe in future, others will see the need. Or maybe I'll remain unique in wanting to use them this way. Time will tell. |
Beta Was this translation helpful? Give feedback.
-
An example where I ran into this is with But the more I thought about it, the less convinced I've been that it's actually a smart thing to do to use the same name. Probably always better to leave the names to the judgment of the call site. |
Beta Was this translation helpful? Give feedback.
-
Re-opening this as a growing number of other related requests refer to it. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
(This proposal is a rewording of a comment from #415)
Currently, as of v7.1, the compiler can "project" tuple names across expressions. However, this "breaks down" the moment a tuple with unnamed elements is introduced. For example, the following code won't compile:
There is a use case where being able to preserve names across a tuple with unnamed elements "boundary" and that is when method chaining with methods that take a generic tuple as a parameter.
This can be explained without using a complex method chaining example with the following simple code example:
As
ValueTuple<T1, T2>
is astruct
, it isn't possible to just useBar<T>
and constrainT
using generic constraints. The only current solution is to use an unconstrainedT
, but then the method can't be constrained to a tuple and the elements are not accessible from withinBar
.UPDATE
To give a possibly clearer example, take the following extension method for
(T1, T2)
:Beta Was this translation helpful? Give feedback.
All reactions