Skip to content

proposal: Go 2: make named return parameter assignment on last line a terminating statement of a function #31630

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
StephanVerbeeck opened this issue Apr 23, 2019 · 25 comments
Labels
FrozenDueToAge LanguageChange Suggested changes to the Go language Proposal v2 An incompatible library change
Milestone

Comments

@StephanVerbeeck
Copy link

Consider the following 2 alternatives for implementing the same function that returns a result value. The second syntax is my favorite and I use it throughout my code (I am currently developing a commercial accounting application in GOlang (based on lxn/walk)).

func Sum(a, b int) int {
	return a + b
}

func Sum(a, b int) (total int) {
	total = a + b
}

But when using the predefined variable it seems very illogical to me that GO complains about leaving out the final return statement so I must correct this by doing this:

func Sum(a, b int) (total int) {
	total = a + b
	return
}

My proposal is to NOT generate the "missing return at end of function" error in the latter case allowing programmers to leave out this obviously superfluous line of code.

@odeke-em odeke-em changed the title "missing return at end of function" is not always an error proposal: spec: permit no return statement for functions with named return parameters Apr 23, 2019
@gopherbot gopherbot added this to the Proposal milestone Apr 23, 2019
@DeedleFake
Copy link

This is not going to be a popular proposal. There's been a lot of discussion about not even allowing that last example, as it's very easy to write buggy code with it, even with the shadowed during return error that currently exists. Making it possible to not even have a return statement at all is simply a step towards even more potential confusion with basically no benefits.

@randall77
Copy link
Contributor

@DeedleFake What shadowed during return error?

@DeedleFake
Copy link

The following code fails to compile:

func example(t int) (v string) {
	if t > 3 {
		v := 3
		return
	}
	return
}

@odeke-em odeke-em changed the title proposal: spec: permit no return statement for functions with named return parameters proposal: spec: make named return parameter assignment on last line a terminating statement of a function Apr 23, 2019
@StephanVerbeeck
Copy link
Author

StephanVerbeeck commented Apr 24, 2019

The following code fails to compile:

func example(t int) (v string) {
	if t > 3 {
		v := 3
		return
	}
	return
}

sure it does not compile, because it is wrong
why do you declare a new variable with the same name as your output argument?
Function arguments are already declared so there is not need for "var" or" :="

@StephanVerbeeck
Copy link
Author

This is not going to be a popular proposal. There's been a lot of discussion about not even allowing that last example, as it's very easy to write buggy code with it, even with the shadowed during return error that currently exists. Making it possible to not even have a return statement at all is simply a step towards even more potential confusion with basically no benefits.

ok that is a valid argument but it depends also on your viewpoint.
In go you have 3 places where you can declare a function argument

  • before the function name if the function is a method of that argument type
  • behind the function name for the input arguments
  • behind the input argument for the output arguments

If you consider a function to be a lambda expression then there are just input arguments and output arguments but not necessarily a return. Why treat input arguments different than the output arguments.
Does anybody of you feel the need to write

func MyExample(a,b,c int) (d,e int) {
	receive a,b,c
	d = a + b
	e = b + c
	return d,e
}

or

func MyExample(a,b,c int) (d,e int) {
	input a,b,c
	d = a + b
	e = b + c
	output d,e
}

or

func MyExample {
	input a,b,c
	d = a + b
	e = b + c
	output d,e
}

No you would not want to write your functions like that. But then what is the logic behind that "feeling".

What is the reason (other then habit of previous millennium) to have an end-of-function indicator at the end of the function? Because I have the feeling that I write an assembler RET instruction at the end of the functions which feels to me totally out of place and out of style in a modern language like GO.

Are we keeping that return statement just because we "feel" we have to keep it?

And please do not suggest to drop the syntax to declare the output arguments with the input arguments (because that was a great idea and I love the consistency that it introduces).

@DeedleFake
Copy link

DeedleFake commented Apr 24, 2019

sure it does not compile, because it is wrong
why do you declare a new variable with the same name as your output argument?
Function arguments are already declared so there is not need for "var" or" :="

I know. I was answering @randall77's question.

And there are plenty of reasons to redeclare a variable with the same name. The most common, probably, is if you have a named error return. For contrived example:

func Example(q bool) (err error) {
  if q {
    file, err := os.Open("file.txt")
    if err != nil {
      // The compiler will catch this for you, even though there's
      // technically nothing wrong with the code, simply because
      // it's highly likely that it's an error.
      return
    }
    defer file.Close()
  }
  // Do something else...
}

@ianlancetaylor ianlancetaylor changed the title proposal: spec: make named return parameter assignment on last line a terminating statement of a function proposal: Go 2: make named return parameter assignment on last line a terminating statement of a function Apr 24, 2019
@ianlancetaylor ianlancetaylor added v2 An incompatible library change LanguageChange Suggested changes to the Go language labels Apr 24, 2019
@ianlancetaylor
Copy link
Contributor

What if there are multiple result parameters?

An explicit return is a good marker for where the result parameters become the actual results. We aren't going to make this change.

@StephanVerbeeck
Copy link
Author

What if there are multiple result parameters?

An explicit return is a good marker for where the result parameters become the actual results. We aren't going to make this change.

Please read your answer again. The result parameters are ALWAYS the actual result, EVEN if you do not assign a value to them! (just try it) Your reasoning is based on a mirage. It is obvious that you have NEVER used the new go syntax with return variables.

@ianlancetaylor
Copy link
Contributor

Please be polite.

There is a distinction between the result parameters and the values returned by a function. You can see this in code like

func F() (x, y int) {
    x, y = 1, 2
    return 3, 4
}

That function returns 3, 4 although the result parameters are set to other values. When the return statement is used with no arguments, it acts as though the current value of the result parameters were the arguments to return. That is, there is an explicit point at which the result parameters become the return values.

I agree that the return statement isn't strictly required, in that we could treat falling off the end of the function as being that explicit point. However, we've decided against that.

@StephanVerbeeck
Copy link
Author

StephanVerbeeck commented May 20, 2019

I never tried that (that should not even be possible).
Is it maybe possible that the change that introduced output arguments next to input arguments was not completely finished (or implemented in a way that allows inconsistent usage).
I have over 40 years lead software development experience (including compilers,parsers,interpreters) and you should not dismiss my insight so swiftly.
I am one of the few people using GO for commercial applications and I NEED the features that I ask for.
This is work for me, not game or hobby!
It is very unexpected to me that mixing the two methods of outputting results is allowed.
e.g. I have functions relying on that bool false is returned when no value is assigned to the boolean output argument.
And I am always polite but I do reserve the right to be upset as I see actions that harm GO community wide (as should you).

// ExifTableModel holds array of exif field name and values
type ExifTableModel struct {
	walk.TableModelBase
	fields []*NamedStringValue // exif field names and values
}

// NewExifTableModel construct table model
func NewExifTableModel(x *exif.Exif) (model *ExifTableModel) {
	model = &ExifTableModel{fields: make([]*NamedStringValue, 0, 100)}
	x.Walk(model)
	sort.SliceStable(model.fields, func(i, j int) bool {
		a, b := model.fields[i], model.fields[j]
		return a.Name < b.Name
	})
	return // <- inconsistent language feature
}

@StephanVerbeeck
Copy link
Author

Also consider Kotlin,Swift,Rust that all have implicit return these days while you stick to the assembler past

@randall77
Copy link
Contributor

e.g. I have functions relying on that bool false is returned when no value is assigned to the boolean output argument.

I'm confused. What exactly are you objecting to? Because you can rely on that. Unless you're worried about a return true statement violating that rule? A return true statement is pretty explicit that it wants to override the named return values.

@StephanVerbeeck
Copy link
Author

I am objecting against the lack of logic that dictates that I have to say to return to the caller at the end of the function. To me that makes as little sense as that I would have to write "enter" at the start of each function to tell the compiler that that is the place to enter the function.
I don't see how you guys can allow such lack of logic and consent with it.
I though programmers were able to understand reasoning better than other people but it seems "we do it because we do it" is the only logic applied here (and that logic sucks)

@randall77
Copy link
Contributor

I believe the intent is that all paths leaving a function (except those functions returning no values) should explicitly say what it wants the results to be. It either says return x, y, z which means it wants those values to be the results, or return which means it wants the named return values to be the results.

I can see your argument. But we've discussed this as far back as issue #320 and decided to go with requiring the explicit signal that the named results should stand.

@ianlancetaylor
Copy link
Contributor

@StephanVerbeeck I apologize if it seems that we are dismissing your insight quickly. We thought about this issue many years ago and explicitly decided that we want it to work as it currently does (thanks to @randall77 for digging up #320, which I forgot about long ago). The "change that introduced output arguments next to input arguments" was made in the earliest stages of language development, well before the open source release in 2009, so this is not new.

This is work for me, not game or hobby!

The same is true of us. In case you didn't know, I personally have been working on Go full time since 2008. I say this not to imply that I am always right, or that I even know what I am talking about, but simply to say that I'm not making decisions casually or lightly.

I should note that when I closed the issue above I was speaking not just for myself, but for the group of people who consider Go language changes. I apologize for not making that clear.

@StephanVerbeeck
Copy link
Author

StephanVerbeeck commented May 21, 2019

I see how things are done.
Nobody listens.
The same issue comes back again and again for 10 years with different people giving EXACTLY the same argument why it is wrong and every time again they are send away with the message "we know better".
Thanks a lot for not paying attention to users, for not responding to the argument stated (oh yes. you missed that part completely didn't you?).
And 10 years being wrong does not make it right.
The same issue will come back argued by others so don't assume this will ever be settled.

This is how C# came into existence, java community not accepting changes and microsoft walked away from the table. Then when C# overclassed JAVA and finally the JAVA community itself was tired of it and developed themselves Kotlin. The same frustration out of which GO was born, exists now against the people freezing it into a death standard.

@ianlancetaylor
Copy link
Contributor

I'm sorry you feel frustrated. Every language decision has costs and benefits. There are rarely clear decisions.

That said, this particular issue is only a question of whether a naked return is required at the end of a function with named result parameters. I honestly don't think this will be the issue that creates Go#.

@StephanVerbeeck
Copy link
Author

I don't agree with that, and even some of the argument you used is obviously wrong.

  1. Looking back at Language should not demand a return statement when using named return values #320 I see that THERE WAS NO PREVIOUS ARGUMENT the issue was closed without proper motivation or reasoning (other than flawed).
  2. There does not seem to be a GO community, the amount of responses on the items marked as open decision questions is almost non-existing (4 people are active at best on each topic and from the answers it seems that none gives the current topics proper attention so the entire GO github scene seems to be run by not more than 3 to 5 people who decide all on their own).
  3. The naming of the output variables is not for documentation purposes. The compiler generates code to allocate, initialize and cleanup those. So not using them should be considered a BUG and throw a compiler ERROR (multiple times demanded by users).
  4. When not naming these variables then they do not exist (by default) so this is a BIG language issue and not "a minor syntax help". Without this it is not possible to write decent 1 liner functions (used as lambda expressions).
  5. Using named parameters and then not using them should throw an error of unused variables (which they literally are).
  6. Using named parameters and then not using them should be considered BAD practice and not "something I feel good with". Introducing such an invitation feature to write buggy code is hardly something to feel good about!!!!
  7. This topic should not be decided by vote or how you feel but by CONSISTENCY and LOGIC. That the same syntax is used for input as for output function arguments but that completely different rules apply is a GRAVE MISTAKE.
  8. You closed this issue because you do not want to do the work. (assign the issue to me for implementation or at least open it again so that others can SEE it)

@ianlancetaylor
Copy link
Contributor

  1. Looking back at Language should not demand a return statement when using named return values #320 I see that THERE WAS NO PREVIOUS ARGUMENT the issue was closed without proper motivation or reasoning (other than flawed).

I don't agree that the reasoning on #320 was flawed.

  1. There does not seem to be a GO community, the amount of responses on the items marked as open decision questions is almost non-existing (4 people are active at best on each topic and from the answers it seems that none gives the current topics proper attention so the entire GO github scene seems to be run by not more than 3 to 5 people who decide all on their own).

The fact that most people don't care about this issue does not mean that there is no Go community. See an issue that people care about, such as #15292 or #19412.

  1. The naming of the output variables is not for documentation purposes. The compiler generates code to allocate, initialize and cleanup those. So not using them should be considered a BUG and throw a compiler ERROR (multiple times demanded by users).

People do in practice use result variable names as documentation, among other things. If this causes the compiler to generate additional code, then perhaps that is a bug to be fixed. I don't see a clear reason to change the language for this.

That said, can you point us to the multiple times that users have demanded this change?

  1. When not naming these variables then they do not exist (by default) so this is a BIG language issue and not "a minor syntax help". Without this it is not possible to write decent 1 liner functions (used as lambda expressions).

I'm not sure I understand. You can write a one line function using a return statement.

  1. Using named parameters and then not using them should throw an error of unused variables (which they literally are).

Is this the same as point 3?

  1. Using named parameters and then not using them should be considered BAD practice and not "something I feel good with". Introducing such an invitation feature to write buggy code is hardly something to feel good about!!!!

But people really do name result parameters for documentation purposes, and that is not wrong.

  1. This topic should not be decided by vote or how you feel but by CONSISTENCY and LOGIC. That the same syntax is used for input as for output function arguments but that completely different rules apply is a GRAVE MISTAKE.

The same rules apply. There is no compiler error for naming an input argument and not using it.

  1. You closed this issue because you do not want to do the work. (assign the issue to me for implementation or at least open it again so that others can SEE it)

I closed the issue because we are not going to change the language as you suggest. Please do not make an unfair accusation like saying that it is because I do not want to do the work. If we thought that the language change was acceptable, and we didn't want to do the work, we would simply leave the issue open for others to work on.

@StephanVerbeeck
Copy link
Author

StephanVerbeeck commented May 22, 2019

That said, can you point us to the multiple times that users have demanded this change?

1 = mine
2 = #320 Comment 2 by thwilloch:
I think that with named returns you should forbid return with values.
Then you can easily accept functions without return statements.

That the same conclusion was reached twice should be a clue that the argument has a valid foundation.

The same rules apply. There is no compiler error for naming an input argument and not using it.

That is correct but actually not what I intended to imply. My failure in communicating my idea is that I assume we have a common background, but now I see I will have to build up my argument more elaborate and clearly. So let me take my argument buildup step-by-step.

There are 3 places where we can put a function argument in go:

type rectangle struct {
	x, y, w, h int
}
type rectangleSize int

func (size *rectangleSize) size1(r *rectangle) {
	*size = rectangleSize(r.w * r.h)
}

func size2(r *rectangle, size *rectangleSize) {
	*size = rectangleSize(r.w * r.h)
}

func size3(r *rectangle) (size rectangleSize) {
	size = rectangleSize(r.w * r.h)
}		

the functions size1,size2,size3 are identical but size3 does not compile unless I write is as follows

func size3(r *rectangle) (size rectangleSize) {
	size = rectangleSize(r.w * r.h)
	return
}

The core of my reasoning is that there are multiple places where I can declare my output argument.
It seems obvious (to me) that it is a GOOD thing to separate input arguments from output arguments (like in size3). However can you give me one good reason why the compiler INSISTS that I keep using the assembler statement RETURN to do output. What is the motivation (if any) for requiring a STATEMENT to do output and not requiring a statement to do INPUT?

Why is it that the most recent programming language is still locked in this assembler-age concept?

So what I fail to grasp is how the introduction of named output arguments at the same time did NOT make the old-style return statement optional. Now it is a mix of two concepts neither of which is consistent. Please accept my proposal as language change to make the return statement OPTIONAL.

I have looked at all the other issues that were referred here and did not found a spot where this discussion was held or argued. So yes maybe there was an issue with identical text long ago but that does not mean that the issue was properly addressed at that time. Closing this issue should have been accompanied by an argument or reference to an argument but I see neither.

I accept that the same issues can not be repeated over and over (given the number of open issues) but, unless it is given proper attention it will remain a structural weakness in the language.

@StephanVerbeeck
Copy link
Author

StephanVerbeeck commented May 22, 2019

I just realized that the return statement is ALREADY optional.
But only for some functions and not for others.
So from now on I am going to write RETURN at the end of ALL MY FUNCTIONS.
Does that make sense to you ( I hope not ).

type rectangle struct {
	x, y, w, h int
}
type rectangleSize int

func (size *rectangleSize) size1(r *rectangle) {
	*size = rectangleSize(r.w * r.h)
	return
}

func size2(r *rectangle, size *rectangleSize) {
	*size = rectangleSize(r.w * r.h)
	return
}

func size3(r *rectangle) (size rectangleSize) {
	size = rectangleSize(r.w * r.h)
	return
}		

@ianlancetaylor
Copy link
Contributor

I'm glad that you have found an approach that can work for you, even though it is imperfect.

@StephanVerbeeck
Copy link
Author

hmmm to be honest I have more serious concerns for the moment.
I have spend 6 hours today benchmarking GOlang performance and have come up with figures that seriously deviate from publications I have seen so far.
The performance of channels is abominable.
The performance of GC is abominable.
The blocking of go routines is abominable.

In an hour we have a staff meeting about canceling the entire multi platform development (windows, mac, iOS, android) of PilotSpace.
The windows accounting app can go forward but the major project for which this was a test-case will probably be reverted back to C++ based on these figures

@randall77
Copy link
Contributor

The performance of channels is abominable.
The performance of GC is abominable.
The blocking of go routines is abominable.

If you have benchmarks that demonstrate these performance problems, please open issues including your benchmark code so we can take a look.

@StephanVerbeeck
Copy link
Author

If you have benchmarks that demonstrate these performance problems, please open issues including your benchmark code so we can take a look.

I can not spend time on this anymore but feel free to replicate the test #32195

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

No branches or pull requests

5 participants