goroutine returns

1,546 views
Skip to first unread message

John Asmuth

unread,
Jun 7, 2011, 3:08:53 PM6/7/11
to golan...@googlegroups.com
Although I couldn't find any topic with the google groups search, I feel like this has been discussed before, if not recently.

I think it would be of great convenience if a goroutine launch returned a channel that sent something of the function's return type.

That is, 
func foo(...) X { ... }
...
var ch chan X = go foo(...)

equiv to
func gofoo(...) (ch chan X) {
   ch = make(chan X)
   go func() {
     ch <- foo()
   }()
   return
}
...
var ch chan X = gofoo()

Of course, for multi-returns (which must be named):
func foo(...) (x X, y Y) { ... }
...
var ch chan struct {x X; y Y} = go foo()

equiv to
func gofoo(...) (ch chan struct {x X; y Y}) {
   ch = make(chan struct {x X; y Y})
   go func() {
     var rval struct {x X; y Y}
     rval.x, rval.y = foo()
     ch <- rval
   }()
   return
}
...
var ch chan X = gofoo()

For functions with no return values it would be an empty struct.
func foo(...) { ... }
var ch chan struct{} = go foo()

As I said, this would only be a convenience, but people very often need to write the same infrastructure (with minor name changing) to wait for completion of goroutines. The following code is much nicer than wrapping the called functions and setting up a wait infrastructure.

ch1 := go foo1()
ch2 := go foo2()
ch3 := go foo3()
<-ch1
<-ch2
<-ch3

Thoughts?

- John

bflm

unread,
Jun 7, 2011, 5:56:57 PM6/7/11
to golang-nuts
On Jun 7, 9:08 pm, John Asmuth <jasm...@gmail.com> wrote:
> Thoughts?

Sorry, but -1 from me. It would make the (implicit version of the) go
statement (probably noticeable) heavier, both semantically and - I
guess - also in the run time performance. IMO the go statement should
have no "value" at all. It is not a [coroutine] call with a value
return/yield-like behaviour.

Andrew Gerrand

unread,
Jun 7, 2011, 6:24:42 PM6/7/11
to golang-nuts
This makes "go" an expression, rather than a statement. That means you
can launch a goroutine anywhere a channel value is expected. IMO
launching a goroutine is too significant an operation to allow it to
be obfuscated. You're trading a small convenience in for a big
difference in readability.

Andrew

Steven

unread,
Jun 7, 2011, 8:20:38 PM6/7/11
to golan...@googlegroups.com
On Tue, Jun 7, 2011 at 3:08 PM, John Asmuth <jas...@gmail.com> wrote:
Although I couldn't find any topic with the google groups search, I feel like this has been discussed before, if not recently.

I think it would be of great convenience if a goroutine launch returned a channel that sent something of the function's return type.

That is, 
func foo(...) X { ... }
...
var ch chan X = go foo(...)

equiv to
func gofoo(...) (ch chan X) {
   ch = make(chan X)
   go func() {
     ch <- foo()
   }()
   return
}
...
var ch chan X = gofoo()

ch := make(chan X)
go func() { ch <- foo() }() 

Not only is this very simple, but its also more flexible.

Of course, for multi-returns (which must be named):
func foo(...) (x X, y Y) { ... }
...
var ch chan struct {x X; y Y} = go foo()

equiv to
func gofoo(...) (ch chan struct {x X; y Y}) {
   ch = make(chan struct {x X; y Y})
   go func() {
     var rval struct {x X; y Y}
     rval.x, rval.y = foo()
     ch <- rval
   }()
   return
}
...
var ch chan X = gofoo()


ch := make(chan struct{x X; x Y})
go func() {
x, y := foo()
ch <- struct{x X; x Y}{x, y}
}()

This is the only case that can be tidied up, since there's no way to initialize a struct directly from a multiple return. If you give the type a name, it becomes substantially simpler, and you could define an adaptor function if you wanted. A special case of the composite literal could work to improve this, but I'm not sure how generally useful it would be. All it really saves you from is writing names for all the return values (twice), which there aren't normally all that many of.

For functions with no return values it would be an empty struct.
func foo(...) { ... }
var ch chan struct{} = go foo()


ch := make(chan struct{})
go func() { foo(); ch <- struct{}{} }()
 
As I said, this would only be a convenience, but people very often need to write the same infrastructure (with minor name changing) to wait for completion of goroutines. The following code is much nicer than wrapping the called functions and setting up a wait infrastructure.

ch1 := go foo1()
ch2 := go foo2()
ch3 := go foo3()
<-ch1
<-ch2
<-ch3

ch := make(chan struct{})
go func() { foo1(); ch <- struct{}{} }()
go func() { foo2(); ch <- struct{}{} }()
go func() { foo3(); ch <- struct{}{} }() 

for i := 0; i < 3; i++ {
<-ch
}

OR

func SignalDone(wg *sync.WaitGroup, fns ...func()) {
        wg.Add(len(fns))
for _, fn := range fns {
fn := fn
go func() { fn(); wg.Done() }()
}
}
...
var wg sync.WaitGroup
SignalDone(&wg, foo1, foo2, foo3)
wg.Wait()

John Asmuth

unread,
Jun 7, 2011, 9:28:14 PM6/7/11
to golan...@googlegroups.com
Certainly a compiler could optimize away calls that aren't on a RHS, but it was just an idea and I'm happy to let it get voted down.
Reply all
Reply to author
Forward
0 new messages