Go: proposal: Go 2: multivalue switch

Created on 22 Jul 2020  路  11Comments  路  Source: golang/go

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.

Go2 LanguageChange Proposal Proposal-FinalCommentPeriod

Most helpful comment

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

All 11 comments

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

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.

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.

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.

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" }:
        // ...
}

Would like to see this implemented

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

Language change, so leaving in Go 2 process.

Yeah, my bad.

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

No further discussion.

Was this page helpful?
0 / 5 - 0 ratings