5 ms·
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 someo
by S_A_P 9y ago
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 9y agoMultiple 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 9y agoMultiple 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 9y agoFor 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 9y agoAgreed. 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 9y agoYeah, 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 9y ago> 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 9y agoNot 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.
- dvlsg 9y agoPossibly a Result in F#? Is Scala Try the equivalent of an Either monad which automatically catches underlying exceptions from Java interop?
- bunderbunder 9y agoTo an approximation it's that. It also makes for somewhat more readable code since the properties are "result" and "error" rather than "left" and "right". I've never liked to see much use of Either because it has the same readability issues as tuples.
- lkitching 9y agoI don't see how the tuple is better in this case since the first thing you need to do is check the flag to see if the value is valid. The tuple version then needs to be split over two lines instead of a single line like if(TryGetSomething(3, out MyType outvar)) { ... }
- seanmcdirmid 9y agoout vars are nice because you don't have to change the return type of the method you are editing. Tuples are kind of a drastic change that you can only easily undertake on initial design. Also, for your example, Option<MyType> tryGetSomething(int param) is probably better than either.
- paavohtl 9y agoWith C# 7 out vars are IMO actually now much more ergonomic to use than tuple returns. if (TryGetSomething(int param, out var value)) { // use it here } If you returned a tuple you'd have to destructure both variables, and have a separate line for the if and the declaration.
- Someone1234 9y agoList<Type> or similar means you're creating a lot of types in a large project. Specifically you're making a ton of Dtos or similar objects just to bundle values together and transfer them between layers. You can use anonymous objects, but then the return type is simply "object", which isn't descriptive enough. Plus anonymous objects are flagged to not cross assembly boundaries without wild hacks. Tuples work in situations where you'd otherwise use a small Dto, which is then combined with a larger Dto further up the food chain. And since it is self-descriptive you're not losing much clarity over the small Dto.
- redmand 9y agoI'll use them for data passing within a class. The calling method needs multiple values of differing types and I can't personally justify creating a class just to encapsulate those values for a call or two. So I tuple. But that tuple stays within that class and is never exposed or stored.
- arwhatever 9y agoGood call on the encapsulation. I've encountered the occasional public API with methods that require some type like a Tuple<int, int, string>, and got pretty peeved. And go ahead and name the argument "values" or something similar, for good measure.
- PJDK 9y agoThe new tuples do allow names, which can be a nice half way house so you can do something like (bool success, string errormessage, foo addedItem) as a return type. A big step up from Tuple<bool, string, foo>!
- barrkel 9y agoDatabase query results. Any query beyond the most trivial has a schema that depends on the query, and does not map to any entity other than the types of the expressions in the select clause. Tuples, combined with a nice API (I'm more familiar with jOOQ than LINQ these days, but it should apply to LINQ too) mean you preserve type safety when working with your query results.
- thedirt0115 9y agoIt's been a while since I've used C#, but I used to use tuples when I wanted simple composite-key dictionaries -- typically grouping things up for batch processing, etc. Before LINQ's convenient multiple-key GroupBy and other stuff, you could emulate with Dictionary<Tuple<T1,T2>,T3>.
- martinni 9y agoCould be as simple as a query. (Value, TimeTaken) or something along those lines.
- dtech 9y agoTuples are useful when the types are different, in that case a dictionary or list doesn't work since a list(dog, fish) will be of type list<animal>, while a tuple (dog, fish) will be Tuple2<Dog, Fish>.