Skip to content

Correctly pass multiple return values as arguments to other functions #973

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

Closed
gopherbot opened this issue Jul 28, 2010 · 13 comments
Closed

Comments

@gopherbot
Copy link
Contributor

by Erlan.Sergaziev:

What steps will reproduce the problem?
1. Compile this program
func main() {
    fmt.Printf("String: %s, Int: %d\n", multipleReturnValues())
}

func multipleReturnValues() (s string, i int) {
    return `String`, 10
}

What is the expected output?
String: String, Int: 10

What do you see instead?
Compile error:
multiple-value multipleReturnValues() in single-value context
cannot use multipleReturnValues() as type interface { } in function argument


What is your $GOOS?  $GOARCH?
linux, amd64

Which revision are you using?  (hg identify)
14 July Release

Please provide any additional information below.
It might be difficult to implement, but logically the compiler should know that the
function returns multiple values and pass them as separate arguments to Printf.
@rsc
Copy link
Contributor

rsc commented Jul 29, 2010

Comment 1:

I think this used to work.  I also think the spec allows it.  I'd have to check both.

Owner changed to r...@golang.org.

Status changed to Accepted.

@robpike
Copy link
Contributor

robpike commented Aug 24, 2010

Comment 2:

The text of "..." isn't crystal clear, but this is:
As a special case, if the return parameters of a function or method g are equal in
number and individually assignable to the parameters of another function or method f,
then the call f(g(parameters_of_g)) will invoke f after binding the return values of g
to the parameters of f in order.
No other special cases for function calls are allowed.  Therefore, since Printf takes a
string argument first, the program here is erroneous.  It's debatable whether the spec
admits Printf(multipleReturnValues()) or Println(multipleReturnValues) but I think the
answer is still  no and in any case that narrow case is not what the report is about.
In short, addressing this requires a change to the spec.  It should be clarified to
prevent or perhaps admit even the narrower interpretation.

Status changed to Thinking.

@robpike
Copy link
Contributor

robpike commented Aug 24, 2010

Comment 3:

I missed one part: "If f has a final ... parameter, it is assigned the return values of
g that remain after assignment of regular parameters."
Therefore the narrower case is explicitly permitted, but the general case is still not.

@rsc
Copy link
Contributor

rsc commented Aug 24, 2010

Comment 4:

I misread the original program.  Printf(f()) and Println(f())
are allowed and tested and known to work. 
Printf("foo", f()) is not.  The special case is only when
the multi-return function is the only argument, as Rob points out.

Status changed to WorkingAsIntended.

@gopherbot
Copy link
Contributor Author

Comment 5 by Erlan.Sergaziev:

I am slowly getting it - Go's no Ruby :).
Anyhow, thanks for clarifying this!

@brendensoares
Copy link

I expected this to work as well.

If this works:

package main

import "fmt"

func Init(mode, revelPath, basePath string) (string, string, string) {
    return mode, revelPath, basePath
}

func ParsePaths(importPath, srcPath string) (mode, revelPath, basePath string) {
    mode = "prod"
    revelPath = "/revel"
    basePath = "/"
    return
}

func main() {
    fmt.Println(Init(ParsePaths("/import", "/src")))
}

then it's logical that this does too:

package main

import "fmt"

func Init(mode, revelPath, basePath string) (string, string, string) {
    return mode, revelPath, basePath
}

func ParsePaths(importPath, srcPath string) (revelPath, basePath string) {
    revelPath = "/revel"
    basePath = "/"
    return
}

func main() {
    fmt.Println(Init("prod", ParsePaths("/import", "/src")))
}

but it does not :(

Can we make this work?

@brendensoares
Copy link

To be clear:
If we know ParsePaths returns 2 values, why can't the compiler expand the values in the function call of Init.

@griesemer
Copy link
Contributor

@brendensoares It's certainly not "logical" - it's up to the rules of the spec, and the spec doesn't allow it, which is why the compiler doesn't do it.

Please move language discussions to golang-nuts not the issue tracker. Also, the time for language changes has passed long ago. Thanks.

@brendensoares
Copy link

@griesemer sorry, but when exactly did the time for improving Golang pass?

Perhaps "logical" is not the best word, but rather "pragmatic" in that it is reasonable to expect the binding of arguments to return values to work consistently no matter the location of the arguments in the function's signature.

@ianlancetaylor
Copy link
Contributor

@brendensoares As Robert said, the issue tracker is not the place for this kind of discussion. We use the issue tracker for bug reports, proposal, tracking changes, and the like. Thanks.

@griesemer
Copy link
Contributor

@brendensoares https://golang.org/doc/faq#language_changes . And, for the reference, at GopherCon 2014, Rob Pike made the comment explicitly: "The language is done".

Also, you are making the assumption that your suggestion is an "improvement". That is far from certain. To be clear, the current rules were carefully thought out and are deliberate. You can be assured that we thought about your suggestion during the design of the language (between 2007 and 2009). If your suggestion were permitted, what about f(g(), h()) where both g and h return multiple values that happen to match the parameter list of f? Should probably be permitted as well. And then we should also allow things like: "a, b, c, d := f(), g()" otherwise we have another inconsistency. And so forth. Permitting this is of course possible, but it also opens the door for unexpected errors. For instance, the parameter lists of the functions may change in unexpected ways and the code may still work, hiding errors. So the pragmatic solution here is to allow a small set of rules that are reasonably safe, and not to allow everything that might be possible.

But again, please move this discussion elsewhere. If you have a concrete proposal, please follow the proposal process: https://github.com/golang/proposal .

Thanks.

@brendensoares
Copy link

@griesemer thanks. I've posted to golang-dev and chatted on freenode #go-nuts to get some insight. Your comment above is by far the most help I've received so far into understanding the possible underlying reasons that this functionality was excluded.

Any idea if what @robpike said regarding adding clarification to the spec ("It should be clarified to
prevent or perhaps admit even the narrower interpretation.") will be acted on?

@griesemer
Copy link
Contributor

@brendensoares @robpike mentioned later: "Therefore the narrower case is explicitly permitted, but the general case is still not." Per the spec.

So there's no reason to make any spec changes. If there's no concrete issue filed (proposal, or bug) it's unlikely that it will get any attention. We do make minor spec adjustments, either to clarify a situation which is not well documented, or to remove an inconsistency (one part of the spec says one thing, another part says something else). In extremely rare cases we loosen the spec (e.g., most recently we allowed the omission of the key type for elements of map composite literals if the key was a composite literal as well.

This is not one of those cases. It's been fairly uncontroversial, the compilers all agree, and the spec seems reasonably clear.

Hope that helps. And again, please move this discussion elsewhere if you are still not satisfied, or follow the proposal process. Thanks.

@golang golang locked and limited conversation to collaborators Sep 23, 2016
@rsc rsc removed their assignment Jun 22, 2022
This issue was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants