Skip to content
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

Proposal: Deconstruct overloads for Array<T> #3215

Closed
gsfreema opened this issue Feb 19, 2020 · 11 comments
Closed

Proposal: Deconstruct overloads for Array<T> #3215

gsfreema opened this issue Feb 19, 2020 · 11 comments

Comments

@gsfreema
Copy link

Other languages such as javascript and python provide the ability to deconstruct an array into named variables. One example where this can be useful is when you have a string with known parts and a separator. A simple example would be a key and value separated by a semicolon. In C#6, someone could write the following code.

var parts = s.Split(';');

if (parts.Length > 1)
{
    var k = parts[0];
    var v = parts[1];
    // use k and v
}

C#7 introduced ValueTuple and Deconstruction so this could be condensed.

var parts = s.Split(';');

if (parts.Length > 1)
{
    var (k, v) = (parts[0], parts[1]);
    // use k and v
}

Taking this one step further with Deconstruct overloads on Array would allow for this code.

var parts = s.Split(';');

if (parts.Length > 1)
{
    var (k, v) = parts;
    // use k and v
}

Given that arrays can have various lengths, Array would need to have many overloads with 1 to N out parameters to have this feature work. It should be easy enough to justify an upper limit on out parameters because too much deconstruction is just bad code. Also, tuples do something similar already where there is a limit on the number of items they can have.

There is also a question as to what happens if you're trying to deconstruct an array into more variables than there are items in the array. The code you would write by hand today would check the length before accessing items in the array so you don't run into an ArgumentOutOfRangeException. I would expect the same to happen here. If you're trying to deconstruct into 3+ variables and you only have 2 items in the array, you should receive an ArgumentOutOfRangeException.

@HaloFour
Copy link
Contributor

Since such an operation is inherently fallible that should be implemented as a pattern rather than as deconstruction. Recursive variable patterns would allow you to deconstruct the elements into variables (or perform further match operations) on the individual elements.

I know that there are existing proposals for array/list/collection patterns, I will try to dig them up.

@HaloFour
Copy link
Contributor

See #1039

@HaloFour
Copy link
Contributor

Also note that you can implement deconstructors as extension methods, so you can already achieve this functionality without a language or runtime change. However, as deconstruction is considered infallible you don't have a lot of good choices for handling when an array doesn't have enough elements. At best you could throw, which is far from a good experience.

SharpLab

@Pyrdacor
Copy link

Wouldn't it better to add a Split or TrySplit that returns a Tuple? And this as an extension method of your own lib? Cause a split can not know the length of the resulting array and it seems very specific to your input and use case.

If such deconstruction would be implemented I would prefer a general approach to add a "ignore the rest" operator for all deconstructions. Like:

(var x, _*) = someValue;
(var y, _+) = someValue;

@CyrusNajmabadi
Copy link
Member

If you're ignoring the rest... Why not write:

var x = someValue.First()

?

@Pyrdacor
Copy link

Pyrdacor commented Feb 28, 2020

If you're ignoring the rest... Why not write:

var x = someValue.First()

?

Cause:

var (x, y, z, _*) = someArrayOrBigTuple;

Moreover this does not have to be a collection at all. It could be a custom deconstructor or Tuple.

And maybe even this would be possible:

var (x, _, z, _*) = someValue;

@CyrusNajmabadi
Copy link
Member

This proposal is for arrays though...

@Pyrdacor
Copy link

Pyrdacor commented Feb 28, 2020

This proposal is for arrays though...

Please read my above message. I said:

If such deconstruction would be implemented I would prefer a general approach to add a "ignore the rest" operator for all deconstructions. Like:

(var x, _*) = someValue;
(var y, _+) = someValue;

This general approach would be more useful for tuples and custom destructors but it would also solve the given issue for arrays.

@HaloFour
Copy link
Contributor

@Pyrdacor

This general approach would be more useful for tuples and custom destructors but it would also solve the given issue for arrays.

The compiler knows what method to call based on the arity of the deconstruction. A spread operator there throws that completely out the window. It also doesn't work well for arrays in that case because the compiler has no idea how many elements are in the array. For arrays (and other lists) I think deconstruction makes little sense, that's something much better suited to a pattern where you can recursively match on the contents. That's also where I think a spread/discard pattern makes sense.

@Pyrdacor
Copy link

@Pyrdacor

This general approach would be more useful for tuples and custom destructors but it would also solve the given issue for arrays.

The compiler knows what method to call based on the arity of the deconstruction. A spread operator there throws that completely out the window. It also doesn't work well for arrays in that case because the compiler has no idea how many elements are in the array. For arrays (and other lists) I think deconstruction makes little sense, that's something much better suited to a pattern where you can recursively match on the contents. That's also where I think a spread/discard pattern makes sense.

I agree. Patterns would be a much better approach without the risk of runtime exceptions. That's why I said: If such deconstruction would be implemented I would prefer...

@YairHalberstadt
Copy link
Contributor

Closing as championed between #3245 and #4082

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants