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: spec: select: multiple case expressions or fallthrough keyword #23196

Closed
pciet opened this issue Dec 20, 2017 · 9 comments
Closed

proposal: spec: select: multiple case expressions or fallthrough keyword #23196

pciet opened this issue Dec 20, 2017 · 9 comments
Labels
FrozenDueToAge LanguageChange Proposal v2 A language change or incompatible library change
Milestone

Comments

@pciet
Copy link
Contributor

pciet commented Dec 20, 2017

Unlike switch, select can only have one expression per case which may force code duplication or a separated function. This proposal is to add the option of multiple select case expressions.

select {
case <-cancel, <-w.(http.CloseNotifier).CloseNotify():
    // cancel action
}

Instead of:

select {
case <-cancel:
    // cancel action
case <-w.(http.CloseNotifier).CloseNotify():
    // cancel action
}

An example with assignment:

cancel := make(chan struct{})
cancelWork := make(chan int)
concurrentA(cancel)
concurrentB(cancelWork)
select {
case <-cancel, k := <-cancelWork:
    // unused variable assignments are set to zero value
    if k != 0 {
        // handle work
    }
    // cancel action
}

In more detail, the proposal is that multiple expressions for a select case is equivalent to individually cased expressions with a duplicated code block, plus the requirement of all assignments used in the single code block.

My use case is a web page where a browser close and a cancel button press cause the same cancel to occur in a concurrent request. In my case there are only two lines of code duplicated:

// https://github.com/pciet/wichess/blob/master/web_competitive15.go#L112
select {
case <-rdy:
	return // the client redirects to a GET /competitive15
case <-cancel:
	competitive15Matcher.Cancel(name)
	http.NotFound(w, r) // the client ignores the POST response
	return
case <-w.(http.CloseNotifier).CloseNotify():
	competitive15Matcher.Cancel(name)
	http.NotFound(w, r)
	return
}

The cases are different (CloseNotify doesn't need the http.NotFound) but the idea remains. Originally posted to golang-nuts: https://groups.google.com/forum/#!topic/golang-nuts/ROxbuskAglc

@gopherbot gopherbot added this to the Proposal milestone Dec 20, 2017
@mvdan
Copy link
Member

mvdan commented Dec 20, 2017

If you have case x := <-c1, y := <-c2, what happens?

@mvdan mvdan added v2 A language change or incompatible library change LanguageChange labels Dec 20, 2017
@pciet
Copy link
Contributor Author

pciet commented Dec 20, 2017

Both variables would have to be used in the case block:

select {
case x := <-c1, y := <-c2:
    if x != false {
        // x non-zero value received
        fmt.Println(x)
        break
    }
    // y non-zero value received
    fmt.Println(y)
}

Comma ok is another case too:

select {
case x, ok := <-c1, y, ok := <-c2:
    // doesn't compile because of repeated ok var?
select {
case x, okX := <-c1, y, okY := <-c2:

Maybe since ok is always a bool it can have the same name:

select {
// still has a lot of commas
case var1, ok := <-c1, var2, ok := <-c2, var3, ok := <-c3:
    if ok == false {
        return
    }
    if var1 != 0 {
        fmt.Println(var1)
        break
    }
    if var2 != nil {
        fmt.Println(var2)
        break
    }
    fmt.Println(var3)
case <-c4, <-c5, <-c6:
case <-done:
    return
}

There's the problem of the zero-value being sent on the chan meaning the block can't figure out which var to use.

@neild
Copy link
Contributor

neild commented Dec 20, 2017

Your example use case could be written without duplication as:

select {
case <-rdy:
	return // the client redirects to a GET /competitive15
case <-cancel:
case <-w.(http.CloseNotifier).CloseNotify():
}
competitive15Matcher.Cancel(name)
http.NotFound(w, r) // the client ignores the POST response
return

@pciet
Copy link
Contributor Author

pciet commented Dec 20, 2017

That makes sense. I don't have a good example use case.

@pciet
Copy link
Contributor Author

pciet commented Dec 21, 2017

@mvdan despite the Go 2 tag do you think this could be a Go 1 feature? It doesn't seem like a breaking change.

@mvdan
Copy link
Member

mvdan commented Dec 21, 2017

There will be no more significant changes to the language before Go 2.0, so I would say very unlikely.

@ofpiyush
Copy link
Contributor

Our use case is similar to @pciet's example.

We have a event or tick case, both trigger the same behaviour.

We ended up moving that into a function. Felt bad to allocate resources on every function call.

I'll try @neild's solution and report if we land into some other problem.

A hypothetical case which wouldn't work this way:

eventAchan and ticker trigger one behaviour.

eventBchan, a closed channel and timeout trigger another behaviour.

@pciet
Copy link
Contributor Author

pciet commented Jan 18, 2018

fallthrough for switch is something I wasn't aware of. Adding this keyword to select could have the same effect. Here's an old golang-nuts thread about that: https://groups.google.com/forum/#!topic/golang-nuts/j0RobXhmGac

@pciet pciet changed the title proposal: spec: select: multiple case expressions proposal: spec: select: multiple case expressions or fallthrough keyword Jan 18, 2018
@ianlancetaylor
Copy link
Contributor

When used with sends on a channel, there is no way to tell which send succeeded in a case. When used with receives that happen to receive the zero value, there is no way to tell which receive succeeded in a case (particularly important since receives from a closed channel get the zero value). The same functionality can also be easily obtained with our current select by factoring out the case bodies.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge LanguageChange Proposal v2 A language change or incompatible library change
Projects
None yet
Development

No branches or pull requests

6 participants