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
Labels
Milestone

Comments

@carnott-snap
Copy link

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
@martisch martisch added v2 A language change or incompatible library change LanguageChange labels Jul 22, 2020
@randall77
Copy link
Contributor

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

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 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

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 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

Would like to see this implemented

@carnott-snap
Copy link
Author

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

@ianlancetaylor
Copy link
Contributor

Language change, so leaving in Go 2 process.

@carnott-snap
Copy link
Author

Yeah, my bad.

@ianlancetaylor
Copy link
Contributor

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

@ianlancetaylor
Copy link
Contributor

No further discussion.

@golang golang locked and limited conversation to collaborators Sep 15, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

6 participants