-
Notifications
You must be signed in to change notification settings - Fork 1k
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
zero- and one-element tuples #883
Comments
What's the purpose of a zero-element tuple? |
Purpose.. probably to satisfy a desire for a unit type/value that some functional languages have (as opposed to 'void' that C#/.net has), and some language's common convention of using empty collections or zero-valued tuples to represent nothing, aka unit. |
There might also be a need to support them conceptually to permit deconstruction of zero and one element patterns, e.g. |
As @mattwar says, However, I feel it would be a bad idea to introduce yet another type ( void Foo(bool b) => b ? Console.WriteLine("True!") : (); Having said that. I'm not really convinced that void Foo(bool b) => b ? Console.WriteLine("True!") : void; but I may be missing some use-case for it. |
I'm thinking that they are, wanting to be able to treat tuple result = SomeFunction();
switch( result )
{
case Is (): /* roughly MayBe.None */
case Is (var A): /* roughly MayBase.Some<T> */
case Is (var A, var B):
} with () xor (T) SomeFunction()
{
If( condition )
{
return ( Value );
}
else
{
return ();
}
} |
If you have:
How would you call it? Would it implicitly convert |
I like how Apple Swift does it. A single-element tuple (womple) is just an alternative syntax for a scalar value. An |
@HaloFour even if it does boil down to |
I consider this a feature though. 😆 |
A real unit type would be very useful for generic types. Take a look on tasks. We have there two types in C#, one is For tasks this is already to late. But think of async enumerables. If |
I agree that a real unit type would be useful. If it's feasible though, then I'd like to see Of course, it may not be practicable. In that case, I think the next best solution is a unit Foo()
{
Console.WriteLine("foo");
} // no return needed as unit has no value to return |
@DavidArno |
But let's assume we're willing to put any amount of effort into it. What would it do, exactly? Lack a |
So basically, exactly what I described here. |
|
It appears to be the best what we can get. And I like it. The following feels very natrual: async IAsyncEnumerable<unit> ToAsyncEnumerable(IEnumerable<Task> list)
{
foreach (var t in list)
{
await t;
yield return;
}
} and: // T can be unit!
async IAsyncEnumerable<T> ToAsyncEnumerable<T>(IEnumerable<Task<T>> list)
{
foreach (var t in list)
{
yield return await t;
}
}
Probably. |
Non-generic |
If there is no result being produced for each MoveNext, why would anyone want to call |
@jm2, an Let's say you want to write an email to a list of recipients via var lastOrder = DateTime.Now + TimeSpan.FromSeconds(5);
var orders = recipients
.Select(r => SendEmailAsync(r)) // due to lazy evaluation the mails are not sended now
.ToAsyncEnumerable() // The (extension) method in my last post
.TakeWhile(_ => DateTime.Now < lastOrder);
foreach await (var _ in orders)
{
// We can write this line after every sended email
// not just when all emails are sended.
Console.WriteLine($"Email sended at {DateTime.Now}");
} |
I don't think one-element tuples should be in any way "equivalent" to the value itself. If you have any other tuple, it's just a container type and to get the actual value, you have to dot into it (and can even give it a custom name). The logical extension of this would be to do the same for a one-tuple. Why should it be any different? |
Tuples are only somewhat containers in the C# language. You have to dot into them out of necessity, but in some cases the language does (or will) treat them just as separate values and ignore that container. For example, the compiler already completely skips the container if you immediately deconstruct a tuple literal. This is despite the fact that the (int, int) GetPoint(int x, int y) => (x, y);
(float, float) GetPointF(float x, float y) => (x, y);
var x = GetPoint(1, 2);
var y = GetPointF(1.0f, 2.0f);
if (x == y) { ... }
// converted to
if (x.Item1 == y.Item1 && x.Item2 == y.Item2) { ... } IIRC, in math a "womple" is just the single value. In various programming languages (e.g. Swift) that's also the case. There are probably advantages and disadvantages to both approaches. I'm not particularly vested in either, but I'd like to hear scenarios where womples are actually useful/necessary. |
@HaloFour My point is you still have to deconstruct it to get the value. var a = (item1: 1, item2: 2, item3: 3).item1; // OK
var b = (item1: 1, item2: 2).item1; // OK
var c = (item1: 1).item1; // error? That would really be unexpected for me for the one-tuple to behave completely differently. I can't think of any usage for a 1-tuple, I'm more interested in 0-tuples, and it would definitely be weird to have 0 and not 1. I think they should be allowed even if it's just for consistency. Also, if you do go all the way down to zero: var a = (item1: 1, item2: 2, item3: 3); // type == ValueTuple
var b = (item1: 1, item2: 2); // type == ValueTuple
var c = (item1: 1); // type == int ?
var d = (); // type == ValueTuple In your example, a one dimensional point equality would just be translated to
C# is not math (and I'm quite happy about that) 😄 |
I'd like I've found a way of serializing the tuple property names, and it's quite annoying not to be able to use "single value + name" tuples. |
Func<(T1 x1, T2 x2)>
Func<(T1 x1)>
Func<()> // I really want this
...
Task<(T1 x1, T2 x2)>
Task<(T1 x1)>
Task<()> // I really want this
... |
Will we suport deconstruct inline single value tuples? Today in C#8 beta we suport it only in pattern matching For exemple public abstract class Option<T> { }
public class None<T> : Option<T> { }
public class Some<T> : Option<T>
{
public T Value { get; }
public Some(T value) => Value = value;
public void Deconstruct(out T value) => value = Value;
} This works: if (maybeFoo is Some<Foo>(var value))
{
// value is a value of type Foo
...
} but this wont if (maybeFoo is Some<Foo> someFoo)
{
var (value) = someFoo;
...
} I think this is something thats should work |
I'd like to see this too. I'm putting together a scripting framework that wraps .NET's scripting library, and I'm considering letting scripts declare tuples for expected input (e.g. |
This would be nice in general, it could allow for code such as this: Dictionary<string, (int color)> dict;
// later
var color = dict["Table"].color; Compared to just |
I think that code isn't clear because you called it |
I like |
This was very confusing when I attempted use a Deconstruct() with a single out param, |
single element tuples should be able to be named as it makes good quick objects in generics Also regarding tuple syntax while wrapping a value inside braces is valid it doesn't actually do anything unless it is a logical expression. Just check whether the whole expression is wrapped if it is then its a tuple if its only partially wrapped it's not. there is no reason to do something like... var value = (x * y + z == 0 ? 0 : 1) However it is still useful to do things like.. var value = (x * y + z) == 0) ? 0 : 1
var tuple = ((x * y + z) == 0) ? 0 : 1)
var value = x * (y + z)
var tuple = (x * y + z) |
@Shadowblitz16 That code already has meaning and exists today in many projects. We cannot break it by having its meaning change. |
I wonder if target typing could be used when passing a single-value tuple via a method call or doing an assignment. That approach was used recently to make the ternary operator more usable. It would be unfortunate if I had to write this code: (int Item1) GetInfo() {
return (Item1: 2);
} When the compiler could infer what I mean with this code: (int Item1) GetInfo() {
return (2);
} But, even if I do have to write (int Count, int _unused) GetInfo() {
return (2, 0);
} Using a I also admit that I think that Python’s way of solving this problem with a trailing comma is acceptable. I do not expect this to be acceptable to the C# community because it looks wrong:
|
Another use-case for supporting shorthand single-element tuples:
Effectively allows named arguments for callbacks, as the arguments are all just one tuple with names. Works fine until you need a callback with just one argument:
Would be nice for this to work. As I mentioned in #6636 a possible way to avoid ambiguity would be to allow trailing commas in tuples, hence a single- and zero-element tuple could be:
@binki And I've just noticed you said the same thing at the end of your post. 😄 I don't agree that it looks wrong - C# has trailing commas allowed in various places already such as array initializations. |
@gafter commented on Fri Jan 06 2017
To best align positional pattern-matching (which won't restrict the number of values being deconstructed) with tuples, please support zero-element and one-element tuple types and tuple expressions.
@AdamSpeight2008 commented on Fri Jan 06 2017
Doesn't the current tuple parser require a minimum of 2 values?
@alrz commented on Fri Jan 06 2017
@AdamSpeight2008 Yes.
@bbarry commented on Fri Jan 06 2017
Why not align the other way, don't recognize deconstruction syntax with fewer than 2 elements?
@HaloFour commented on Fri Jan 06 2017
@gafter
I assume that this will be a post-C# 7.0 consideration?
@bbarry
Without support for conditional deconstruction of zero/one values then patterns like
None()
andSome(T)
would be impossible.@orthoxerox commented on Sat Jan 07 2017
@bbarry Single-value tuples are necessary for primitive wrapper types.
@dsaf commented on Mon Jan 09 2017
Would leveraging it for naming primitive return values be considered an abuse?
instead of
@HaloFour commented on Mon Jan 09 2017
I like how Swift handles "womples", they're effectively just the scalar value itself. So an
Int32
is an(Int32)
is an((Int32))
. This makes sense as1
,(1)
and((1))
are all the same value. Swift does not allow for naming the element of a "womple", though, which kind of makes sense.@Richiban commented on Tue Jan 31 2017
Will the zero-tuple be explicitly referred to as unit anywhere?
I'd love to see some support for this in the future, such as being able to use
Func<int, ()>
instead ofAction<int>
, for example.@alrz commented on Tue Jan 31 2017
I think the type (and literal) for unit could be
()
if it wasn't ambigious, but using it in place of void probably requires language support so you dont need to explicitly return()
.@jcouv commented on Tue Sep 05 2017
@gafter Is this tracking a compiler/API change or a language feature? If the latter, could you close or move to csharplang?
The text was updated successfully, but these errors were encountered: