Skip to content

Instantly share code, notes, and snippets.

@empijei
Last active October 24, 2018 05:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save empijei/a9665ac5e3059671be229acee8826798 to your computer and use it in GitHub Desktop.
Save empijei/a9665ac5e3059671be229acee8826798 to your computer and use it in GitHub Desktop.
Feedback on Go2 Generics proposal: syntax

As Liam Breck said, changing the function signature by adding more round parentheses would make it very hard to read it. I personally found something like

func (/*receiver*/)Foo(/*types*/)(/*args*/)(/*return tuple*/){

c,d := a.Foo(Type)(e,f)

to be way too complex compared with the usual go syntax.

I read the section on characters and the following ones, but I don't think that justifies the loss on readability.

Generics are already a complex thing by themselves, without making them hard do read. I think generics should be used as little as possible, and we should provide something that highlights there is an unusual feature being exploited. If the need is having a bounded lookahead in the compiler, even replicated characters could fit the purpose. Reading something like

func(/*receiver*/)Foo<<<Type>>>(/*args*/)(/*return tuple*/)

c,d := a.Foo<<<Type>>>(e,f)

makes it very clear that we are using a generic, gives room for the eyes to adjust and understand what is going on, and since <<< is not a valid sequence just requires a bounded lookahead. (This is also true for many other ascii characters, like pipes or backticks)

I'm not saying this one with < should be the syntax we end up with, but please think about alternatives. Generics are going to save a lot of lines of code, I don't think adding 4 more characters per use/declaration is going to be an issue.

NOTE: this would also look better in my opinion

func(/*receiver*/)Foo`Type`(/*args*/)(/*return tuple*/)

c,d := a.Foo`Type`(e,f)
@davecheney
Copy link

I like the way D did it;

func (/*receiver*/) Foo!Type(/*args*/)(/*return tuple*/)

@divan
Copy link

divan commented Sep 1, 2018

I'm still proposing using emoji for generics syntax – nice, clean and hard to type :)

@kalexmills
Copy link

For your viewing pleasure, here is a concrete example of what a generic function header might look like for a generic function defined with a receiver...

func (ks *KeyStore) Put(type K, V)(key K, val V) (bool ok, err error) {
  /* ... implementation goes here ... */
}

This seems much too busy, and I expect it will motivate programmers to break up function signatures in large codebases.

It would be preferable in my view to just tersely declare a type as generic wherever it is first used in the signature. I think appending or prepending ? is nice and evocative.

func (kvs *KVStore) Put(key ?K, val ?V) (bool ok, err error)

A comma-delimited list of contract declarations can follow the return value. gofmt could even linewrap them by default to keep the function header uncluttered.

func (kvs *KVStore) Put(key ?K, val ?V) (bool ok, err error)
     Equal(K) {

}

These are just examples of alternatives and I'm not strongly advocating for them as much as pleading for relief from an excessive four sets of parentheses in function headings.

@peter-mckenzie
Copy link

It would be preferable in my view to just tersely declare a type as generic wherever it is first used in the signature. I think appending or prepending ? is nice and evocative.

A problem with that approach are things like factory methods, where the parameterized type(s) only appear in the return value of the function's signature. How/where does one pass the type parameters when invoking the function?

@deanveloper
Copy link

deanveloper commented Sep 6, 2018

func (/*receiver*/)Foo(/*types*/)(/*args*/)(/*return tuple*/) ...

I'd like to mention that this is not allowed by the current proposal, as functions with receivers may not have their own type-list (for reflection reasons)

@gocs
Copy link

gocs commented Oct 24, 2018

Biggest and the most Important use of generics (imo) is data structures like list, iterators, promises, and observables.

The drawback is on the function parameter, reciever, and the return which consumes multiple parenthesis and line width (actually).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment