Go: proposal: Go 2: remove redundant struct specifier on methods declarations

Created on 29 Dec 2018  ·  8Comments  ·  Source: golang/go

The standard way to declare the method for some struct is the next:

type SomeStruct struct {
    someArg interface{}
}

func (s *SomeStruct) somePointerBasedMethod() {}

func (s SomeStruct) someValueBasedMethod() {}

If there are only few methods in the struct - it is ok, but if you have relatively big collection of methods - duplication of the struct specifier goes boring very quickly. For the structures which names are long - the problem goes further and significantly increases the code base size and decreases readability, for example:

type SomeVeryLongNameStructWithComplexLogic struct {
    someArg interface{}
}

func (s *SomeVeryLongNameStructWithComplexLogic) somePointerBasedMethod() {}

func (s SomeVeryLongNameStructWithComplexLogic) someValueBasedMethod() {}

The proposal is to simplify the syntax to the next way:

type SomeStruct struct {}

// Method of struct "SomeStruct", 
// because it is first struct reached on the direction 
// from the method declaration to the beginning of the file.
//
// 99% of code is written in the manner when struct-related methods 
// are going right after the related struct. There is no need for huge code duplication.
//
// "*s" after keyword `func` automatically specifies that method's receiver is a pointer.
// At the same time it names the receiver to "s".
func *s somePointerMethod() {
    s.someArg = noop()
} 

// For value based receivers - there is the same logic.
func s someValueMethod() {
    s.someArg = noop()
}

The presence of an argument between the keyword func and the method's name automatically indicates that this method belongs to the struct. We do not need any parentheses or redundant struct names specified.

With this improvement the code for long named structs would look like this:

type SomeVeryLongNameStructWithComplexLogic struct {}

func *s someMethod() {}

func *s someOtherMethod() {}

The code for the cases when there are several structs defined in common file:

type StructA struct {}

func *s method() {}

func *s other() {}

// etc ...

type StructB struct {}

func *s method() {}

func *s other() {}

One more important thing here is the potential performance boost.
Compiler would be able to identify corresponding struct significantly faster with this syntax, because it would be able to simply store the _last seen struct_ and attach all methods seen further to it automatically. No more any receiver's name parsing needed.

FrozenDueToAge Go2 LanguageChange Proposal

Most helpful comment

This is very transparent logic. There is no ambiguity here.

The parser should know how to parse a top-level declaration just by looking at each token. Remembering earlier type declarations, and peeking at tokens to see if there's an open parentheses, is precisely what would make the Go syntax much more complex. Just from looking at func foo it's completely ambiguous what the parser should do, as I explained above.

There is no mental collision here.

Your new proposed syntax is very similar to an existing syntax that does something very different - I'm only pointing that out.

And code base shortening seems to be the good way to reach it.

We agree to disagree. Languages that try to be fast to write by saving characters at all costs tend to be the less readable ones in the long run.

All 8 comments

Code I contribute to often has long type names (on the order of the example) and writing them over again is not a big issue (vim-go or other editor plugins help). I think the added syntactic sugar shown above doesn't provide much value for the cost of having two ways to write the same thing.

This would also add ambiguity to the language syntax. When the parser sees func foo, does it:

  • parse foo as a function name, e.g. func foo() { ... }
  • parse foo as this new receiver syntax, e.g. func foo bar() { ... }

Go has very few ambiguities like those at the moment, so I think we should be very careful when adding more.

Another point against this change that I see is that we already have the func (foo) bar() { ... } form, but it means a different thing - in there, foo is the receiver type, not name. I think this syntax would complicate the language for readers. Remember that Go should be easy to read and understand, not so much to write quickly.

For the record: since any type can have methods, this should clearly apply to any type, not just struct types. (But I think it is unlikely that we would adopt this change.)

@mvdan

This would also add ambiguity to the language syntax. When the parser sees func foo, does it

Actually no.
The parser should only check where the token with parentheses appears (the name of the function) and then check if there is a receiver defined before it. This is very transparent logic. There is no ambiguity here.

@crhntr

vim-go or other editor plugins help

As I know, golang tries to be tool-independent.
It is great that many editors has support for this kind of code duplication problem, but from the language perspective it seems to be wrong solution.

On early days of the language the core team declared that even code highlighting would be not needed for the golang due it's tiny and well composed syntax. My proposal follows the idea that language should be simple for writing and reading without any kind of additional tools.

Another point here is the next: in case if this kind of syntax was introduced by the language reasonable earlier, what kind of 2 types to write methods of structs would you choose today: shorter or longer?

@mvdan

Another point against this change that I see is that we already have the func (foo) bar() { ... } form, but it means a different thing - in there, foo is the receiver type, not name. I think this syntax would complicate the language for readers.

In this case foo is wrapped by the parentheses, and it clearly indicates, that it is not a receiver. There is no mental collision here.

Remember that Go should be easy to read and understand, not so much to write quickly.

Agree that "Go should be easy to read and understand".
And code base shortening seems to be the good way to reach it.
Less code to read == less time for mental processing, no?

This is very transparent logic. There is no ambiguity here.

The parser should know how to parse a top-level declaration just by looking at each token. Remembering earlier type declarations, and peeking at tokens to see if there's an open parentheses, is precisely what would make the Go syntax much more complex. Just from looking at func foo it's completely ambiguous what the parser should do, as I explained above.

There is no mental collision here.

Your new proposed syntax is very similar to an existing syntax that does something very different - I'm only pointing that out.

And code base shortening seems to be the good way to reach it.

We agree to disagree. Languages that try to be fast to write by saving characters at all costs tend to be the less readable ones in the long run.

This would certainly make readability worse, in exchange for the dubious benefit of saving a few keypresses when typing code. A non-starter, in my view.

Currently all Go top level declarations are stand alone. This would break that feature. It would mean that introducing a new type definition between existing methods could completely break the program.

The resulting code seems less readable.

This means that either you can't move a method declaration to a different file, or there are two different syntaxes for method declaration which seems like unnecessary duplication.

If the concern is long type names for readability, you can always use an alias for the method declarations.

Thanks, but we aren't going to make this change.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

OneOfOne picture OneOfOne  ·  3Comments

natefinch picture natefinch  ·  3Comments

gopherbot picture gopherbot  ·  3Comments

ashb picture ashb  ·  3Comments

myitcv picture myitcv  ·  3Comments