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: Go 2: multivalue switch #40353

Closed
carnott-snap opened this issue Jul 22, 2020 · 11 comments
Closed

proposal: Go 2: multivalue switch #40353

carnott-snap opened this issue Jul 22, 2020 · 11 comments

Comments

@carnott-snap
Copy link

@carnott-snap carnott-snap commented Jul 22, 2020

description

Many functions use multivalue returns, which are non-trivial to integrate into switch statements. I propose adding support to switch statements for accepting multivalue inputs:

switch path.Split(p) {
case ("path/to", "file"), ("path/to", "dir"):
	// ...
default:
	// ...
}

extensions

I would think we also want to allow people to specify pseudo-tuples inline:

one, two := true, false
// ...
switch (one, two) {
case (true, false):
	// ...
default:
	// ...
}

Since this is effectively destructuring, we should probably allow usage of the _ symbol:

switch path.Split(p) {
case (_, "file"):
	// ...
default:
	// ...
}

pitfalls

This does not play nice with errors.Is, since switch only uses runtime equality checking and operator overloading is not supported, however the old syntax would work fine:

s, err := strconv.Atoi(in)
switch {
case errors.Is(ErrXxx):
	// ...
case s == "string":
	// ...
default:
	// ...
}

costs

This is effectively syntax sugar for:

dir, file := path.Split(p)
switch {
case dir == "/path/to" && file == "file":
	// ...
default:
	// ...
}

The complexity to the parser and compiler could be non-trivial, but this remains to be seen. I feel the readability of destructuring is a real win.

@gopherbot gopherbot added this to the Proposal milestone Jul 22, 2020
@gopherbot gopherbot added the Proposal label Jul 22, 2020
@randall77
Copy link
Contributor

@randall77 randall77 commented Jul 22, 2020

You can kind of do this today:

package main

func main() {
	a, b := g()
	type stringPair struct{ x, y string }
	switch (stringPair{x: a, y: b}) {
	case stringPair{x: "foo", y: "bar"}:
		println("hello")
	}
}

func g() (string, string) {
	return "foo", "bar"
}
@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Jul 22, 2020

Can you point to a couple of examples of existing code that would benefit from this feature? It's not obvious to me that this comes up very often.

@carnott-snap
Copy link
Author

@carnott-snap carnott-snap commented Jul 22, 2020

It has come up internally, so I cannot point to public source. More abstractly, this is useful when consuming multivalue returns where none of the elements are errors[0] and there are distinct categories for values. I think the path processing is a decent example, but a get date-time would fit too:

func now() (year, month, date uint)
switch now() {
case (_, 1, _):
        // if january
case (2020, _, _):
        // if 2020 (and not january)
}

0] Technically any non-trivial check that needs to be performed is also problematic. This includes <, >, or function calls like errors.Is. It would be nice for switches to support this too, but this seems like scope creep, the syntax escapes me, and it would be non-trivially complex.

@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Jul 22, 2020

Thanks. I understand that this is potentially useful. But it also introduces new concepts into the language that don't already exist, namely the parenthesized tuples in case statements. And we all agree that it doesn't provide any capabilities that can't already be done; it's just some syntactic sugar.

Every change has a cost, and here the cost is new syntactic constructs that don't exist anywhere else. It leads to questions like "why can't I write chan (int, string) and c <- (1, "a")"? That is, the new syntactic construct is not orthogonal.

So given that cost, I think we need to see a pretty strong benefit. One way to show a benefit is to show existing code that would use this.

@carnott-snap
Copy link
Author

@carnott-snap carnott-snap commented Jul 23, 2020

I am unclear how best to assess the impact to existing code, since the workarounds are pretty heterogeneous, and my anecdotes will not build a strong case for impact. One thought would be to look for the number of multireturn functions that contain 2+ non-error values. There will be some false positives, but it would indicate an upper bound on impact.

I tried to pull structure from the current return syntax, func do() (string, error), but it is not a perfect fit since that supports names like (foo string, err error). I agree there is the possibility for confusion, but think a tuple type is the best way to clarify this.

Alternatively, it may be better to design a functional match/guard syntax that supports full type destructuring, but this is way into scope creep territory.

var s struct{ x, y string }
// ...
switch s {
case { x: "foo" }:
        // ...
}
@Dontmindmes
Copy link

@Dontmindmes Dontmindmes commented Jul 24, 2020

Would like to see this implemented

@carnott-snap
Copy link
Author

@carnott-snap carnott-snap commented Aug 6, 2020

@ianlancetaylor: can you add this to the proposals project?

@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Aug 6, 2020

Language change, so leaving in Go 2 process.

@carnott-snap
Copy link
Author

@carnott-snap carnott-snap commented Aug 6, 2020

Yeah, my bad.

@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Aug 11, 2020

Per discussion above, this is a likely decline. Leaving open for four weeks for final comments.

@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Sep 15, 2020

No further discussion.

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

Successfully merging a pull request may close this issue.

None yet
6 participants
You can’t perform that action at this time.