Tuples goodness in .NET 4.7.1
tabsoverspaces.com
tabsoverspaces.com
(bool success, string errormessage, foo addedItem)
as a return type. A big step up from Tuple<bool, string, foo>!
instead of "bool TryGetSomething(int param, out MyType outvar)", I rather prefer (bool, MyType) TryGetSomething(int param)
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.Past that, the best tool in an OO language for returning multiple different values is to return a named class created for the task.
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.
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.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.
I've never liked to see much use of Either because it has the same readability issues as tuples.
if(TryGetSomething(3, out MyType outvar)) { ... }Also, for your example,
Option<MyType> tryGetSomething(int param) is probably better than either.
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.
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.
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.
So you are correct to say you don't need .Net 4.7, but you DO need a C# 7 compiler to use System.ValueTuple with .Net 4.5/4.6. We tried to use System.ValueTuple and it would work on VS 2017 but fails to build on VS 2015 due to compiler complaints.
We resolved this by adding the NuGet package "Microsoft.Net.Compilers" to the project. This results in a C# 7 compiler traveling with the solution, and no matter what Visual Studio/installed compiler you have it works.
This won't be a problem in the future as most project templates in Visual Studio come with Microsoft.Net.Compilers preinstalled. Only impacts older projects (such as those targeting .Net 4.5/4.6, etc).
I found myself in a situation exactly like this a while back.
I was given the job of figuring out how to validate data in various stages of an ETL pipeline. I ended up writing a Python program to do the work in a fairly configurable manner.
The key to this working was the fact that tuples in Python can be used as keys for a hash table and are comparable. The program would run different queries in different databases, split the results out into key and value tuples, use the key to store the value tuples in a hash table, and finally do some comparisons.
Since it was all driven by configuration, the code that did the comparison across couldn't know in advance what the data types were or how many columns were involved.
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 :(
Here you go: https://sourceforge.net/p/sasa/code/ci/dotnet-standard/tree/...
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. object x = 99;
if (x is string s))
... do something with 's'
else if (x is int i))
... do something with 'i'For this use case, I mostly use a Result<T> with .Status, .Messages, .Exception, .Data etc properties.
> boxing
You can create a generic function that accepts T where T: ITuple to eliminate this cost.
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?
.NET ports of such libraries can be hugely improved by using value tuples for color representation, while keeping the original array-based semantics.
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.
Now if only we had Rust-style destructuring.
EDIT: I think we do. This makes things a lot easier.
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.
https://blogs.msdn.microsoft.com/ericlippert/2011/06/30/foll...
Based on what I have read below, it's implicit; there's very little that the runtime has to know - it happens at compile time, with the compiler looking for a method with the name "Deconstruct" and the right type signature, and plugging that in during the compile.
The compiler already does a lot of heavy lifting codegen for language features like async / await
https://andrewlock.net/deconstructors-for-non-tuple-types-in...
http://www.techcartnow.com/new-c-sharp-7-features-in-action-...
let (a, b) = (5, 6);
will de-structure the tuple, and assign 5 to a, and 6 to b. This works more generally:
fn foo(tuple: (i32, i32)) {
let (a, b) = tuple;
or even fn foo((a, b): (i32, i32)) {