
Tuples goodness in .NET 4.7.1 - cincura_net
https://www.tabsoverspaces.com/233667-tuples-goodness-in-net-471-net-core-20-included/
======
S_A_P
At risk of embarassing myself, I've yet in my work to come across a _good_ use
case for tuples. I always end up either using a dictionary or list<type>. Can
someone outline a solid use case for tuples? I usually only hear something
like "dynamic types". In thinking out loud, I guess maybe it could be handy in
API writing, but I would love to learn a new reason.

~~~
pionar
Multiple return values. Using out vars is so clunky to me (and I've been in
.Net for about 10 years now).

instead of "bool TryGetSomething(int param, out MyType outvar)", I rather
prefer (bool, MyType) TryGetSomething(int param)

~~~
SideburnsOfDoom
Multiple return values is useful in a few cases, like the TryDoSomething
pattern with a bool and a value in the success case, and maybe the "Result or
error" pattern.

Past that, the best tool in an OO language for returning multiple different
values is to return a named class created for the task.

~~~
bunderbunder
For the "TryDoSomething" use case, I'd much rather have the function return a
structured generic type. Something similar to Scala's Try[T]. (Is there an
equivalent in F# s standard libraries? I can't remember offhand.)

It ends up being more readable. Safer, too, since, unlike for tuples, you can
refuse to give up a result when the operation failed. It's also more ergonomic
to use, since you can make it mappable. That way you can chain together a
bunch of things that might fail, and pass along any errors, without having to
stick a bunch of extra if-statements into your code.

~~~
SideburnsOfDoom
Agreed. I have seen c# methods that e.g. try and parse to an int, and return
int? - the Nullable generic wrapper, like a rust Option type. You use the null
to mean "not parsed". That works fine for small internal methods.

~~~
bunderbunder
Yeah, though, one annoyance with Nullable<T> in C# is that T must be a value
type. That gets obnoxious when you want to program generically against it,
because, e.g., you can't do have a method like

    
    
      TValue? TryGetValue<TKey, TValue>(TKey key)
    

and have it work with any possible TKey. Unless they've fixed this in the past
couple years, you get stuck writing your own option type to be able to also
work with reference types, which is always fun to try and get past code
review, or you fall back on the out parameters idiom, which is not composable.

~~~
SideburnsOfDoom
> Yeah, though, one annoyance with Nullable<T> in C# is that T must be a value
> type.

Unnecessary because class types already allow null. I would not be surprised
if a method e.g. public Customer TryFindCustomer(int id); returns null if it
can't find the customer.

Of course, if you want to return either a customer or an error details, then
you need another type or generic wrapper.

~~~
bunderbunder
Not strictly necessary, no. But the example I wrote was getting at was meant
to point out that this bifurcation can make it harder to compose generic
types. It's not really an ideal design decision for modern C# so much as a
hold-over from the language's early years, when idiomatic usage favored a much
more imperative style of programming.

------
bonoetmalo
We probably won't switch to 4.7 at work for another year or so but this is
probably the feature people on the floor are most excited about.

So does this mean the tuple preserves the type of its members unlike an
object[]? What's a situation where you would want to take a collection of
objects that don't even share a common interface? (Not bashing tuples, very
curious about the benefits of this.)

Edit: I think I understand tuples when you're using them as return types like
(MyClass class, Exception e), which can already be done with Tuples just
without the nice syntax. But I'm confused about generically taking or
returning an ITuple you know nothing about.

~~~
LandR
I've used tuples in the past when a method could fail and I want the error
code back or I want the thing.

e.g.

(string errorCode, Quax result) TryGetAQuax();

Oh, to have Either / Discrimated Unions in C# :(

We also can't use this at work, we are still having to target 4.5.1 :(

~~~
naasking
> Oh, to have Either / Discrimated Unions in C# :(

Here you go: [https://sourceforge.net/p/sasa/code/ci/dotnet-
standard/tree/...](https://sourceforge.net/p/sasa/code/ci/dotnet-
standard/tree/Sasa/Either.cs)

You can use it like:

    
    
        Either<string, int> x = 99;
        if (x.TryCase1(out var s))
            ...
        else if (x.TryCase2(out var i))
            ...
    

Only up to 4 cases are included because beyond that, I think you should define
a custom type.

~~~
naasking
Although I should note that C# now supports special casting via the 'is'
operator, so you can now just do this:

    
    
        object x = 99;
        if (x is string s))
            ... do something with 's'
        else if (x is int i))
            ... do something with 'i'

~~~
coldacid
There's also the OneOf NuGet package [1] which I've put to good use in a
number of projects. Using `object` and `is` means you can't enforce what types
get passed in, nor can you ensure that all possible types actually get
handled.

[1]:
[https://github.com/mcintyre321/OneOf/](https://github.com/mcintyre321/OneOf/)

~~~
naasking
I can never justify the use of delegates for pattern matching purposes. It's
just way too much overhead, and so would discourage the use of these
abstractions.

------
Shoothe
Note that using the interface ITuple instead of the tuple types directly will
incur boxing (memory allocation for reference type instance).

------
polskibus
How is this feature implemented, what is the complexity of access via index?
Is it O(1)? Also, adding interfaces onto struct and referencing them by
interface causes boxing, right? If so, performance benefit of valuetuple is
erased.

~~~
zamalek
It is implemented with a recursive call (if the tuple contains tuples),
otherwise, it's a hardcoded constant.

> boxing

You can create a generic function that accepts T where T: ITuple to eliminate
this cost.

------
naasking
The base interface for tuples is useful, that's how I did tuples in my Sasa
library years ago.

Can't say I see the need to index a tuple dynamically with an integer, or to
access a tuple as an object[]. What's the use case for this?

~~~
garganzol
A lot of JavaScript color manipulation libraries use arrays to represent a
color. Like c[0] = R, c[1] = G, c[2] = B.

.NET ports of such libraries can be hugely improved by using value tuples for
color representation, while keeping the original array-based semantics.

~~~
naasking
But why keep the original array-based semantics if you're using a tuple?

In fact, I'm not even sure why you'd use a ValueTuple for that at all, because
it's not a tuple but more of a vector type because the elements are all of the
same type. You'd probably want a more meaningful domain-specific type.

------
juliangoldsmith
It's nice to finally have actual syntactic sugar for tuples.

Now if only we had Rust-style destructuring.

EDIT: I think we do. This makes things a lot easier.

~~~
drewmate
Would you mind posting a link or an example of the feature in C#/Rust?

~~~
Analemma_
You can see it in the official announcement about C# 7:
[https://docs.microsoft.com/en-us/dotnet/csharp/whats-
new/csh...](https://docs.microsoft.com/en-us/dotnet/csharp/whats-
new/csharp-7#tuples) (scroll down to the part about the Deconstruct() method)

~~~
drewmate
Thanks. This does look very nice. How does the runtime know if you can
“Deconstruct” an object? Is there an interface that every Object inherits? Or
is it implicit based on whether the object implements the appropriate method?

If the latter, C# is gaining some very golang-like attributes. I also noticed
the special ‘Range’ keyword and Discarded assignments with underscore. I think
this can only be a good thing. C# and go are among my favorite languages to
use and the more good ideas they adopt from the other, the better.

~~~
pauldino
It's implicit based on the appropriate method overload being present. In fact
that's the way foreach loops and LINQ already work in C#, you don't actually
need to implement IEnumerable or IQueryable to use them.

[https://blogs.msdn.microsoft.com/ericlippert/2011/06/30/foll...](https://blogs.msdn.microsoft.com/ericlippert/2011/06/30/following-
the-pattern/)

~~~
drewmate
Ah, that’s interesting. I always just assumed everything I was using inherited
IEnumerable somewhere way down the line. Thought it’s possible that was the
case too. It’s been a few years for me and I didn’t do much outside of
standard library / entity framework.

