Generics and parentheses

22,017 views
Skip to first unread message

Robert Griesemer

unread,
Jul 14, 2020, 5:56:01 PM7/14/20
to golang-nuts

We have received a variety of feedback on the generics draft design (blog). Thanks to everyone who took the time to read it, play with generics code in the playground, file issues, and send us their thoughts.


Not unexpectedly, many people raised concerns about the syntax, specifically the choice of parentheses for type parameter declarations and generic type and function instantiations.


A typical computer keyboard provides four easily accessible pairs of single-character symmetrical "brackets": parentheses ( and ), square brackets [ and ], curly braces { and }, and angle brackets < and >. Go uses curly braces to delineate code blocks, composite literals, and some composite types, making it virtually impossible to use them for generics without severe syntactic problems. Angle brackets require unbounded parser look-ahead or type information in certain situations (see the end of this e-mail for an example). This leaves us with parentheses and square brackets. Unadorned square brackets cause ambiguities in type declarations of arrays and slices, and to a lesser extent when parsing index expressions. Thus, early on in the design, we settled on parentheses as they seemed to provide a Go-like feel and appeared to have the fewest problems.


As it turned out, to make parentheses work well and for backward-compatibility, we had to introduce the type keyword in type parameter lists. Eventually, we found additional parsing ambiguities in parameter lists, composite literals, and embedded types which required more parentheses to resolve them. Still, we decided to proceed with parentheses in order to focus on the bigger design issues.


The time has come to revisit this early decision. If square brackets alone are used to declare type parameters, the array declaration


type A [N]E


cannot be distinguished from the generic type declaration


type A[N] E


But if we are comfortable with the extra type keyword, the ambiguity disappears:


type A[type N] E


(When we originally dismissed square brackets, the type keyword was not yet on the table.)


Furthermore, the ambiguities that arise with parentheses appear not to arise with square brackets. Here are some examples where extra parentheses are not needed with square brackets:


using ()                 using []

func f((T(int))          func f(T[int])

struct{ (T(int)) }       struct{ T[int] }

interface{ (T(int)) }    interface{ T[int] }

[](T(int)){}             []T[int]{}


To test this better understanding, and to get a feel for this alternative notation, we will begin to make changes to our prototype implementation such that it accepts either parentheses or square brackets (only one or the other) in a generic Go package. Those changes will first appear as commits to the dev.go2go branch, and eventually in the playground.


If square brackets don't lead to unforeseen issues, we have another fully explored notation to choose from, which will allow us to make a more informed decision.


- gri, iant


PS: For ambiguities with angle brackets consider the assignment


a, b = w < x, y > (z)


Without type information, it is impossible to decide whether the right-hand side of the assignment is a pair of expressions


(w < x), (y > (z))


or whether it is a generic function invocation that returns two result values


(w<x, y>)(z)


In Go, type information is not available at compile time. For instance, in this case, any of the identifiers may be declared in another file that has not even been parsed yet.


gri

unread,
Jul 14, 2020, 6:29:30 PM7/14/20
to golang-nuts
Correction: The last paragraph in the post below should have said: "In Go, type information is not available at parse time". (Of course, type information is available at compile time.)

Harald Weidner

unread,
Jul 14, 2020, 6:31:55 PM7/14/20
to Robert Griesemer, golang-nuts
Hello,

> A typical computer keyboard provides four easily accessible pairs of
> single-character symmetrical "brackets": parentheses ( and ), square
> brackets [ and ], curly braces { and }, and angle brackets < and >. Go uses
> curly braces to delineate code blocks, composite literals, and some
> composite types, making it virtually impossible to use them for generics
> without severe syntactic problems. Angle brackets require unbounded parser
> look-ahead or type information in certain situations (see the end of this
> e-mail for an example). This leaves us with parentheses and square
> brackets.

Another option would be the introduction of a new two-letter
"bracket" operator, for example <: and :> . This could be parsed
without symbol/type information and even without the "type" keyword.

That said, I'm fine with any syntax. Thank you for your work on
generics!

Regards,
Harald

Doug

unread,
Jul 14, 2020, 9:46:20 PM7/14/20
to golang-nuts
The square bracket-based syntax feels much more readable to me, so it's a welcome change!

Based on my dev background I'm still partial to angle brackets and had an idea for disambiguating function calls. We could put the type information as the first data inside the parens, so a generic function invocation would become:
w(<x,y> z)

This may not be the right forum to discuss the idea, but figured I'd throw it out there.

Thanks,
Doug

Watson Ladd

unread,
Jul 14, 2020, 10:44:53 PM7/14/20
to golang-nuts
Guillamets are worth consideration. They are common on European keyboards and avoid all the syntax ambiguities.

While a technical violation of compatibility by adding new reserved characters, in practice I doubt this will be an issue given the semantics of guillamets.

Sincerely,
Watson

David Riley

unread,
Jul 14, 2020, 11:04:50 PM7/14/20
to Robert Griesemer, golang-nuts
On Jul 14, 2020, at 5:54 PM, 'Robert Griesemer' via golang-nuts <golan...@googlegroups.com> wrote:
>
> But if we are comfortable with the extra type keyword, the ambiguity disappears:
>
> type A[type N] E
>
> (When we originally dismissed square brackets, the type keyword was not yet on the table.)
>
> Furthermore, the ambiguities that arise with parentheses appear not to arise with square brackets. Here are some examples where extra parentheses are not needed with square brackets:
>
> using () using []
> func f((T(int)) func f(T[int])
> struct{ (T(int)) } struct{ T[int] }
> interface{ (T(int)) } interface{ T[int] }
> [](T(int)){} []T[int]{}

Just my $0.02: I really like this. Thank you for responding to feedback, and I look forward to trying the new syntax out!


- Dave

Ahmed (OneOfOne) W.

unread,
Jul 14, 2020, 11:21:11 PM7/14/20
to golang-nuts
This feels a little better, but honestly I'm still all for angle brackets or like Watson suggested, guillamets.

fn(T1)(fn2(T2)(fn3(T3)(v))) // 1
fn[T1](fn2[T2](fn3[T3](v))) // 2
fn<T1>(fn2<T2>(fn3<T3>(v))) // 3
fn«T1»(fn2«T2»(fn3«T3»v)))  // 4

To me, with a background in C++ and Typescript and a little bit of Rust, #3 and #4 are just natural and easier to read.

Regards,
Ahmed W.

faifa...@gmail.com

unread,
Jul 14, 2020, 11:31:35 PM7/14/20
to golang-nuts
One way to distinguish between type A[N] E and type A [N]E is to be more space-sensitive and, for example, disallow whitespace between a type name and the opening square bracket when it signifies type parameters.

Dňa utorok, 14. júla 2020 23:56:01 UTC+2 gri napísal(a):

Ian Lance Taylor

unread,
Jul 14, 2020, 11:37:21 PM7/14/20
to Watson Ladd, golang-nuts
On Tue, Jul 14, 2020 at 7:45 PM Watson Ladd <watso...@gmail.com> wrote:
>
> Guillamets are worth consideration. They are common on European keyboards and avoid all the syntax ambiguities.

https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md#why-not-use

Ian

Ian Lance Taylor

unread,
Jul 14, 2020, 11:39:43 PM7/14/20
to Michal Strba, golang-nuts
On Tue, Jul 14, 2020 at 8:31 PM <faifa...@gmail.com> wrote:
>
> One way to distinguish between type A[N] E and type A [N]E is to be more space-sensitive and, for example, disallow whitespace between a type name and the opening square bracket when it signifies type parameters.

I would be extremely reluctant to make the syntax depend on invisible
whitespace. The syntax already depends on line breaks, but at least
line breaks are very visible. Spacing within a line can be hard to
see and it would be quite easy to make mistakes.

Ian

mac3n

unread,
Jul 15, 2020, 12:03:56 AM7/15/20
to golang-nuts
I'm partial to [], which was used for generics in the Tartan language (1978). For example 

generic func F[Mult: Int](X:Int) y:Int: begin y := Mult * X end

Ian Lance Taylor

unread,
Jul 15, 2020, 12:14:31 AM7/15/20
to Ahmed (OneOfOne) W., golang-nuts
On Tue, Jul 14, 2020 at 8:21 PM Ahmed (OneOfOne) W. <oneo...@gmail.com> wrote:
>
> This feels a little better, but honestly I'm still all for angle brackets or like Watson suggested, guillamets.
>
> fn(T1)(fn2(T2)(fn3(T3)(v))) // 1
> fn[T1](fn2[T2](fn3[T3](v))) // 2
> fn<T1>(fn2<T2>(fn3<T3>(v))) // 3
> fn«T1»(fn2«T2»(fn3«T3»v))) // 4
>
> To me, with a background in C++ and Typescript and a little bit of Rust, #3 and #4 are just natural and easier to read.

The advantage of parentheses is that the language already uses
parentheses for lists in various places. Of course that is also the
disadvantage.

When considering something other than parentheses, I encourage people
to look for objective reasons why one syntax is better than another.
It's going to be different from other aspects of the language. So
what reason would we have for preferring one syntax over another?

For example:

Robert already gave reasons why square brackets are better than angle brackets.

The disadvantage of guillemets is that they are hard to type on many
keyboards. So to me either square brackets or angle brackets would be
better than guillemets.

The disadvantage of a two character sequence such as <: :> is that it
is more typing. So again either square brackets or angle brackets
seem to me to be better.

An example of a reason that square brackets might be a poor choice
would be ambiguous parsing, or cases where the code is harder to read.

It's true that some other languages use angle brackets, but Go already
does many things differently. That is only a minor advantage for
angle brackets. To me at least it does not outweigh the
disadvantages.

In short, please try to provide reasons for a different syntax. "It
looks good" is a valid reason, but please try to explain why it looks
better than square brackets or parentheses.

Thanks.

Ian

robert engels

unread,
Jul 15, 2020, 12:45:41 AM7/15/20
to Ian Lance Taylor, Ahmed (OneOfOne) W., golang-nuts
My opinion is that every major language (no flames please… lots of developers write lots of programs and make money doing it) that supports generics uses < > for generic types, so Go should too - since there is no reason to deviate from this other than to avoid changes to the parser. Seems better to pay this cost once - rather than every Go program that uses generics being harder to read for eternity (especially for those readers that use a lot of languages).
> --
> You received this message because you are subscribed to the Google Groups "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAOyqgcX-OXktNtUs0G4Ns0iEr3R2qLPpU7q1%3DrOY93%3DAO16a3g%40mail.gmail.com.

Paul Johnston

unread,
Jul 15, 2020, 1:06:24 AM7/15/20
to golang-nuts
If the generic expression <T> was always attached/constrained to the "type" or "func" keyword (rather than the type or function name), perhaps this would decrease the lookahead problems with lexing?  For example:

type<T> Point struct {
    x, y int
    data T
}

type<R,S> Transformer interface {
    Transform(R) S
}

func<T> Stringify(s []T) string {
}

type<T> Vector []T

burak serdar

unread,
Jul 15, 2020, 1:12:47 AM7/15/20
to Ian Lance Taylor, Ahmed (OneOfOne) W., golang-nuts
On Tue, Jul 14, 2020 at 10:14 PM Ian Lance Taylor <ia...@golang.org> wrote:
>
> On Tue, Jul 14, 2020 at 8:21 PM Ahmed (OneOfOne) W. <oneo...@gmail.com> wrote:
> >
> > This feels a little better, but honestly I'm still all for angle brackets or like Watson suggested, guillamets.
> >
> > fn(T1)(fn2(T2)(fn3(T3)(v))) // 1
> > fn[T1](fn2[T2](fn3[T3](v))) // 2
> > fn<T1>(fn2<T2>(fn3<T3>(v))) // 3
> > fn«T1»(fn2«T2»(fn3«T3»v))) // 4
> >
> > To me, with a background in C++ and Typescript and a little bit of Rust, #3 and #4 are just natural and easier to read.
>
> The advantage of parentheses is that the language already uses
> parentheses for lists in various places. Of course that is also the
> disadvantage.
>
> When considering something other than parentheses, I encourage people
> to look for objective reasons why one syntax is better than another.
> It's going to be different from other aspects of the language. So
> what reason would we have for preferring one syntax over another?
>
> For example:
>
> Robert already gave reasons why square brackets are better than angle brackets.
>
> The disadvantage of guillemets is that they are hard to type on many
> keyboards. So to me either square brackets or angle brackets would be
> better than guillemets.
>
> The disadvantage of a two character sequence such as <: :> is that it
> is more typing. So again either square brackets or angle brackets
> seem to me to be better.


A two-character sequence like <: T :> may be more typing, but the
two-character sequence like [[ T ]] is easier to type than that and
eliminates the ambiguity. To me, it is easier to read as well, but
that's of course subjective.

>
> An example of a reason that square brackets might be a poor choice
> would be ambiguous parsing, or cases where the code is harder to read.
>
> It's true that some other languages use angle brackets, but Go already
> does many things differently. That is only a minor advantage for
> angle brackets. To me at least it does not outweigh the
> disadvantages.
>
> In short, please try to provide reasons for a different syntax. "It
> looks good" is a valid reason, but please try to explain why it looks
> better than square brackets or parentheses.
>
> Thanks.
>
> Ian
>

Bakul Shah

unread,
Jul 15, 2020, 2:06:34 AM7/15/20
to gri, golang-nuts
I don't much like square brackets or angle brackets or guillemets. But here is a different way
of reducing the need for parentheses at least for the common case:

A proposal for fewer irritating parentheses!

One thing to note is that generic functions & types are *different* from
existing things like const, func, type, var. As such they should have their
own declaration marker. For example

gen T   type pair struct { a, b T } // contrast with type pair(type T) ...
gen T,U type W struct { a T; b U } // contrast with type W(type T, U) ...
gen T   func Print(s []T) {...} // print a slice of T

These function/type/method generics are used by *prepending* the type:

var x int pair // a pair of ints
var y (int, int pair) W // here we have to use parentheses
int Print([]int{1,2,3}) // print a slice of ints
qq := int pair pair{{1,2},{3,4}} // a pair of a pair of ints
ww := (int, int) W pair{{1,2},{3,4}}

This use may seem weird if you are used to C/C++. I find it more readable
than having to deal with extra parentheses. "int pair" clearly says a
pair of ints and so on. What is more, if in future types are allowed to be
*inferred* for generic function calls, you can simply drop the type prefix.

If there is still a parsing ambiguity, I'd suggest adding a - as in int-pair.

Additional type syntax rule:

type: ... | type generic-type| (type-list) generic-type

or

type: ... | type "-" generic-type | (type-list) "-" generic-type

FWIW I thought of this four weeks ago (June 16).

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Fino

unread,
Jul 15, 2020, 2:19:40 AM7/15/20
to golang-nuts
I vote for <: and :> 

type more on traditional keyboard is acceptable for me, since I don't use generics much.

too much parentheses ( and ) in declaration is really hard to read, confused my mind.

square brackets [ and ] should reserve for array and slice.

and, look ahead, we are able to just put <: and :>  on keyboard!

I have made several my own custom keyboard PCB and firmware. Report 2 scan code in one click is easy and simple!  Maximum 6 scancode send once per USB HID protocol. A lot of opensource firmware is there, for ex. https://github.com/qmk/qmk_firmware.  

On touch screen, layout of keyboard can change according to different context. This may happen to desktop keyboard too, with higher cost and more complex physical structure.  

BR fino



Michael Jones

unread,
Jul 15, 2020, 2:26:23 AM7/15/20
to Bakul Shah, gri, golang-nuts
nice. "gen" here is akin to the existing forward declaration of recursive inner functions. it says, "you are about to see something special and you need to know this about it."



--
Michael T. Jones
michae...@gmail.com

Randall O'Reilly

unread,
Jul 15, 2020, 2:54:19 AM7/15/20
to robert engels, Ian Lance Taylor, Ahmed (OneOfOne) W., golang-nuts
Tagging onto this point: while Go deviates from other languages in a number of important ways, syntax at the level of things like parens, brackets, etc is not one of these key differences. It is almost entirely consistent with the "de facto" C-style choices in this respect (to the extent that people mistakenly regard it as just a subset of C). In this context, it does seem a bit of "being different for its own sake", to adopt something *other* than the < > convention for generic types.

Yes, Go should boldly Go where no language has gone before^*, but when it is basically adopting a well-established paradigm widely used in existing languages, doesn't it also make sense to adopt the well-established syntax associated with that paradigm? (although the proposed Go generics differ in some important ways from other languages, the use of type parameters is not one of them.)

While we've been asked to focus on the more "objective" facts of parsing considerations here, the above logic would suggest a different approach: if there is any way to adopt the established conventional syntax and make the parsing work, then that is what should be done. So, essentially, this puts the onus back on the parser programmers to definitively *rule out* the use of < > -- is it really that difficult / costly to do a bit of look-ahead and disambiguate the different use cases?

- Randy

* e.g., see https://github.com/golang/go/issues/39669 for a more radical departure from convention, doing away with the extra parens entirely.
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/825D82DD-C595-415D-B0C6-7BE090A015C7%40ix.netcom.com.

Dan Kortschak

unread,
Jul 15, 2020, 3:01:55 AM7/15/20
to golan...@googlegroups.com
How do these deal with non-type determined uses. Say you have (by any
spelling)

gen F,I func PowN(a F, b I) F {...}

How do you specify the type of F or I when say you have untyped
constants. This still requires parens or other markings to indicate the
actual types of F and I. It seems many of the comments here complaining
about brackets and such are ignoring this aspect of the current
proposal.


Dan Kortschak

unread,
Jul 15, 2020, 3:01:55 AM7/15/20
to golan...@googlegroups.com
On Tue, 2020-07-14 at 23:53 -0700, Randall O'Reilly wrote:
> So, essentially, this puts the onus back on the parser programmers to
> definitively *rule out* the use of < > -- is it really that difficult
> / costly to do a bit of look-ahead and disambiguate the different use
> cases?

The absence of arbitrary look-ahead was from memory one of the early
design intentions of the language.


Bakul Shah

unread,
Jul 15, 2020, 3:37:15 AM7/15/20
to Dan Kortschak, golan...@googlegroups.com
As I wrote, parentheses are not needed for the common case of single type
parameter. If you have more than one, you need parentheses but note that this
is a *prefix* and any parsing ambiguities can removed by using a - if needed.
For your example it will be something like

(float64,int)PowN(1.2, 3)

It’s just a syntax change. There is zero semantic difference from the current
proposal. I just tried to get idea across - I can work out a complete set of
syntax rule changes if there is sufficient interest but the gen rule will be
something like

gen-type-decl: gen-prefix type-decl;
gen-func-decl: gen-prefix func-decl;
gen-prefix: “gen” typeX-list
typeX-list: typeX | typeX-list “,” typeX ;
typeX: NAME [constraint] ;

Max

unread,
Jul 15, 2020, 4:32:21 AM7/15/20
to golang-nuts
I think square brackets are better than parentheses for several reasons:

1. fewer parser ambiguities (see the post that started this thread) - thus fewer cases where programmers must remember "oh, this is a special case, I must put additional parentheses somewhere"
2. programmers can distinguish more easily between a template instantiation `T1[T2]` and a function call `T1(T2)`. Index expressions would have the same syntax as single-type template instantiations `T1[T2]`, but I believe they are somewhat less common than function calls in most code
3. Go already uses a similar syntax for builtin types: `map[K]V`. A generic key/value container would look like `Map[K, V]` which is pretty close

In my Go interpreter, I went a step further and implemented the syntax `T1#[T2, T3]` for generics instantiation. It may not be everybody's favorite, but removes all parsing ambiguities.

An alternative syntax, more similar to other proposals I have seen, is `T1:[T2, T3]` - it could be visually better, but I think it introduces ambiguities in `case` and labels marking `goto` destinations - both already use ':' as delimiter.

Nick Craig-Wood

unread,
Jul 15, 2020, 4:47:40 AM7/15/20
to golang-nuts
In my mind the use of [ ] clashes horribly with the array declaration syntax. When I see [int] in particular my brain says, ah array declaration of size int, what, no wait...

This makes [identifier] mean two different things depending on whether indentifier is a const integer or whether identifier is a type. I think the suffix Vs prefix makes it unambiguous but I'm not sure.

I expect I'd get used to it, but I can see this being difficult for beginners.

Jesper Louis Andersen

unread,
Jul 15, 2020, 5:21:24 AM7/15/20
to Michael Jones, Bakul Shah, gri, golang-nuts
On Wed, Jul 15, 2020 at 8:25 AM Michael Jones <michae...@gmail.com> wrote:
nice. "gen" here is akin to the existing forward declaration of recursive inner functions. it says, "you are about to see something special and you need to know this about it."


The real name is "forall", and it presents a binding site of a type-level parameter.

  forall T, type pair struct { a, b T }

This would bring the notation close to Coq.

Other solutions:

Haskell used capitalized types, String, Bool, Int, ... and lowercase names are parameters, so if you used e.g., 't' in the above, it would implicitly be a type parameter. This is out in Go, due to the capitalization already being used.

OCaml and SML uses a quote prefix to notion the parameter, e.g., the above would be written

type 'a pair = { a : 'a; b : 'a }

and likewise

type 'a option = None | Some of 'a

Yet the current proposal is fine! I think keeping the parser simple is an important goal. Solutions which have whitespace-aware parsing are not good solutions, I think, because they break that the graphical characters are the only which have meaning. They also break in subtle ways when you copy-paste code, some times without parse error but different semantics. Solutions based on < and > irks me for the lookahead needed in the parser. That notation was popularized by C++, and I think the argument that we should just copy it is weak. Go already cleaned up lots of small mistakes in the C-like syntax. If we can clean up type-level parametrization in addition, it would be really nice. We can't get any language evolution if people keep being syntax-conservative. New syntax doesn't automatically apply a gaussian blur to your eyes.


Denis Cheremisov

unread,
Jul 15, 2020, 5:22:51 AM7/15/20
to golang-nuts
Great! Those multiple parenthesis were a beat on unredable side indeed, and I always was in the pro-square party as I always found less and greater signs to be unreadable. As much as I disliked Scala in general I liked their generic syntax. This is a really good news Go will have the same.

среда, 15 июля 2020 г. в 00:56:01 UTC+3, gri:

Jesper Louis Andersen

unread,
Jul 15, 2020, 5:24:40 AM7/15/20
to Nick Craig-Wood, golang-nuts
On Wed, Jul 15, 2020 at 10:47 AM Nick Craig-Wood <ni...@craig-wood.com> wrote:
In my mind the use of [ ]  clashes horribly with the array declaration syntax. When I see [int] in particular my brain says, ah array declaration of size int, what, no wait...

This makes [identifier] mean two different things depending on whether indentifier is a const integer or whether identifier is a type. I think the suffix Vs prefix makes it unambiguous but I'm not sure.


You could make roughly the same argument in a lot of places in the language, e.g.:

The use of () clashes horribly with the expression declaration syntax. When I see f(x) in particular, my brain says, "ah! An expression!, what, no wait!"

This makes (Expr) mean two different things depending on whether it is used to disambiguate the expression AST or in a function call!

Randall O'Reilly

unread,
Jul 15, 2020, 5:28:27 AM7/15/20
to robert engels, Ian Lance Taylor, Ahmed (OneOfOne) W., golang-nuts
Sorry to perseverate and belabor the point, but here's an analogy that makes the point perhaps more strongly and succinctly:

Imagine that, for some reason, Go didn't have the array/slice element operator syntax, [ ], and it was now going to be introduced in Go2, but instead of using this established existing convention, parens were going to be used instead of [ ], due to some parser considerations. I suspect many people would feel the same way they feel about the < > syntax: this is such an established convention that it would seem crazy to not adopt it.

If other languages can get their parsers to deal with this syntax, why not Go? Certainly fast parsing is an important feature, but if anything, readability is perhaps even more important than that?

And to specifically address the example from the original email:

> PS: For ambiguities with angle brackets consider the assignment
>
> a, b = w < x, y > (z)
>
> Without type information, it is impossible to decide whether the right-hand side of the assignment is a pair of expressions
>
> (w < x), (y > (z))
>
> or whether it is a generic function invocation that returns two result values
>
> (w<x, y>)(z)

wouldn't gofmt remove the superfluous (z) in this case (and even with a more complex expression inside the parens, as the > operator would have appropriate precedence?), in existing code? And thus, would it not be reasonable to have a convention in this case that if you have an expression that fits the type parameter interpretation, that interpretation is used instead of the alternative? And if you really wanted to write the alternative, you would be forced to make it explicit with the parens as noted:

a, b = (w < x), (y > (z))

This seems like a very rare case, and a small price to pay for following the established convention..

- Randy

Jesper Louis Andersen

unread,
Jul 15, 2020, 5:54:34 AM7/15/20
to Randall O'Reilly, robert engels, Ian Lance Taylor, Ahmed (OneOfOne) W., golang-nuts
On Wed, Jul 15, 2020 at 11:27 AM Randall O'Reilly <rcore...@gmail.com> wrote:
wouldn't gofmt remove the superfluous (z) in this case (and even with a more complex expression inside the parens, as the > operator would have appropriate precedence?), in existing code?  And thus, would it not be reasonable to have a convention in this case that if you have an expression that fits the type parameter interpretation, that interpretation is used instead of the alternative?  And if you really wanted to write the alternative, you would be forced to make it explicit with the parens as noted:

        a, b = (w < x), (y > (z))

This seems like a very rare case, and a small price to pay for following the established convention..


If the parser cannot disambiguate without type information, then neither can gofmt.

In practice, this means you risk opening pandoras box in several ways:

* gofmt is now part of the system, as you cannot safely parse without it.
* gofmt needs to be more powerful than the parser.
* gofmt must alter the semantics of the program, which goes directly against the idea of gofmt.

The really hard part is that you have to nail all parsing problems in a parser. You can't attack problems on a case-by-case basis as it often yields inconsistency.

Languages in the ML family solves problems like the above by having several steps in the chain:

Lex -> Parse -> Elaborate -> Phase split -> Type Check -> ... -> Machine code

Elaboration is the phase where type information is brought in to disambiguate what the parser found. Phase splitting is also interesting in that it splits work into compile-time and runtime phases (since MLs kind of blends those two things together in its syntax). However, this has implications in the complexity of the parser and language. Go seems to lean more toward a Niklaus Wirth'esque simplicity path; though not as stringent as Wirth would.

Michal Strba

unread,
Jul 15, 2020, 8:41:51 AM7/15/20
to Ian Lance Taylor, golang-nuts
Angle brackets are only problematic in expressions in the bodies of functions when specializing a function or a type, right? They are not problematic in signatures and type definitions.

What about using a dot when specializing in bodies?

    func zero<T>() T {
        var z T
        return z
    }

    func main() {
        x := zero.<int>() // requires a dot
    }

This is less weird than it looks, because Go already uses a dot for type assertions and this is a similar thing.

Jan Mercl

unread,
Jul 15, 2020, 8:44:45 AM7/15/20
to Robert Griesemer, Ian Lance Taylor, golang-nuts
On Tue, Jul 14, 2020 at 11:55 PM 'Robert Griesemer' via golang-nuts
<golan...@googlegroups.com> wrote:

> The time has come to revisit this early decision. If square brackets alone are used to declare type parameters, the array declaration

My 2c - Alternative type parameters syntax (ab)using @$:
https://docs.google.com/document/d/1AoU23DcNxYX2vYT20V2K16Jzl7SP9taRRhIT8l_pZss/edit?usp=sharing

David Riley

unread,
Jul 15, 2020, 9:26:46 AM7/15/20
to Ian Lance Taylor, Ahmed (OneOfOne) W., golang-nuts
On Jul 15, 2020, at 12:13 AM, Ian Lance Taylor <ia...@golang.org> wrote:
>
> The disadvantage of guillemets is that they are hard to type on many
> keyboards. So to me either square brackets or angle brackets would be
> better than guillemets.

Not to mention that, while Go *is* officially in UTF-8, the chance of an output device or editor mis-formatting something that isn't 7-bit ASCII is still reasonably significant even in 2020, and that has much graver consequences for delimiters than for, say, identifiers or string contents. I'm curious about how screen readers react as well; I know we have one or two folks on this list who could probably offer some perspective there.

> The disadvantage of a two character sequence such as <: :> is that it
> is more typing. So again either square brackets or angle brackets
> seem to me to be better.
>
> An example of a reason that square brackets might be a poor choice
> would be ambiguous parsing, or cases where the code is harder to read.
>
> It's true that some other languages use angle brackets, but Go already
> does many things differently. That is only a minor advantage for
> angle brackets. To me at least it does not outweigh the
> disadvantages.

I think square brackets do the job of visually disambiguating the syntax from the other uses of parentheses on the same lines well enough, and it sounds like with the "type" keyword they do the job well enough for the parser as well. Personally, I have no horse in the race for square vs. angle brackets (other than I think it would be foolish to do something technically infeasible or problematic just because it subjectively looks nicer), but then, I also like Objective-C's visual styling, so YMMV.


- Dave

David Riley

unread,
Jul 15, 2020, 9:32:49 AM7/15/20
to Nick Craig-Wood, golang-nuts
On Jul 15, 2020, at 4:47 AM, Nick Craig-Wood <ni...@craig-wood.com> wrote:
>
> In my mind the use of [ ] clashes horribly with the array declaration syntax. When I see [int] in particular my brain says, ah array declaration of size int, what, no wait...

On the other hand, this is already how maps are declared (e.g. map[int]string). It's fairly consistent with that, actually.


- Dave

janeri...@gmail.com

unread,
Jul 15, 2020, 10:59:07 AM7/15/20
to golang-nuts
Go code use a Syntax analog to casting instead of the type keyword.

f.[int](args)
or
var x map[string]GType.[int]

That does not collide with other language features in function declaration, type definitions, inner types in structs or interfaces, variable declarations, type alias declarations and maps / array / chan type declarations.

Georges Varouchas

unread,
Jul 15, 2020, 10:59:08 AM7/15/20
to golang-nuts
Hello all,

I'd first like to say thanks to the go team for their incredible work on delivering such a usable tool as go.

I will then proceed to bring my 2 cents to the World Bikeshedding Foundation :)

I would add a vote to have something more differenciating to indicate "this is a generic instanciation" :

 * for generics delcaration : in both the previous spec and the "soon to be new" spec, the `type` keyword (in `(type ...` or `[type ...`) is a clearly visible marker that this is a generic type declaration
 * for generics instanciation, on the other hand : `(someType ...` or `[someType ...` has no easily visible indicator that this is a generic instanciation, rather than a list of values (when using `(`) or and array/map index (when using `[`)

The square brackets will be much less ambiguous, which is definitely good ;
I would still like to have a more visible thing, so that I don't have to think so much about the context when reading the code :
`[:` or `[!` or `[[` or ...
something which makes the places where generic types are instanciated more unique.

(`[!` could appear as the prefix of an index to a map with bool keys, so probably not this one)

Thanks for your attention,
*Georges*


Le mardi 14 juillet 2020 23:56:01 UTC+2, gri a écrit :

We have received a variety of feedback on the generics draft design (blog). Thanks to everyone who took the time to read it, play with generics code in the playground, file issues, and send us their thoughts.


Not unexpectedly, many people raised concerns about the syntax, specifically the choice of parentheses for type parameter declarations and generic type and function instantiations.


A typical computer keyboard provides four easily accessible pairs of single-character symmetrical "brackets": parentheses ( and ), square brackets [ and ], curly braces { and }, and angle brackets < and >. Go uses curly braces to delineate code blocks, composite literals, and some composite types, making it virtually impossible to use them for generics without severe syntactic problems. Angle brackets require unbounded parser look-ahead or type information in certain situations (see the end of this e-mail for an example). This leaves us with parentheses and square brackets. Unadorned square brackets cause ambiguities in type declarations of arrays and slices, and to a lesser extent when parsing index expressions. Thus, early on in the design, we settled on parentheses as they seemed to provide a Go-like feel and appeared to have the fewest problems.


As it turned out, to make parentheses work well and for backward-compatibility, we had to introduce the type keyword in type parameter lists. Eventually, we found additional parsing ambiguities in parameter lists, composite literals, and embedded types which required more parentheses to resolve them. Still, we decided to proceed with parentheses in order to focus on the bigger design issues.


The time has come to revisit this early decision. If square brackets alone are used to declare type parameters, the array declaration


type A [N]E


cannot be distinguished from the generic type declaration


type A[N] E


But if we are comfortable with the extra type keyword, the ambiguity disappears:


type A[type N] E


(When we originally dismissed square brackets, the type keyword was not yet on the table.)


Furthermore, the ambiguities that arise with parentheses appear not to arise with square brackets. Here are some examples where extra parentheses are not needed with square brackets:


using ()                 using []

func f((T(int))          func f(T[int])

struct{ (T(int)) }       struct{ T[int] }

interface{ (T(int)) }    interface{ T[int] }

[](T(int)){}             []T[int]{}


To test this better understanding, and to get a feel for this alternative notation, we will begin to make changes to our prototype implementation such that it accepts either parentheses or square brackets (only one or the other) in a generic Go package. Those changes will first appear as commits to the dev.go2go branch, and eventually in the playground.


If square brackets don't lead to unforeseen issues, we have another fully explored notation to choose from, which will allow us to make a more informed decision.


- gri, iant


PS: For ambiguities with angle brackets consider the assignment


a, b = w < x, y > (z)


Without type information, it is impossible to decide whether the right-hand side of the assignment is a pair of expressions