Generics and parentheses

21,802 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


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


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


(w<x, y>)(z)


eme...@gmail.com

unread,
Jul 15, 2020, 10:59:10 AM7/15/20
to golang-nuts
Square brackets feel much better. It removes a lot of ambiguity in reading code. 

Actually I personally wouldn't mind to have double square brackets as "Generics" indicator just to remove also what little reading ambiguity they can cause with indices.

type A[[N]]  E

Nic Long

unread,
Jul 15, 2020, 10:59:10 AM7/15/20
to golang-nuts
Square brackets work great. Scala uses them for generics to great effect. They also make me think of Clojure, which introduced them to Lisp for similar reasons to here - to avoid too much overload of regular parentheses - and which people have been very happy with.

This e-mail and all attachments are confidential and may also be privileged. If you are not the named recipient, please notify the sender and delete the e-mail and all attachments immediately. Do not disclose the contents to another person. You may not use the information for any purpose, or store, or copy, it in any way.  Guardian News & Media Limited is not liable for any computer viruses or other material transmitted with or as part of this e-mail. You should employ virus checking software.
 
Guardian News & Media Limited is a member of Guardian Media Group plc. Registered Office: PO Box 68164, Kings Place, 90 York Way, London, N1P 2AP.  Registered in England Number 908396


dylan...@gmail.com

unread,
Jul 15, 2020, 10:59:10 AM7/15/20
to golang-nuts
Here's my 2cents: 
I actually don't mind the parenthesis.. I've written a bunch of functions with them now (
https://github.com/DylanMeeus/hasgo2/tree/master/functions) and they become readable quickly. (Although I also enjoy LISP flavours, so maybe that biases my view of parenthesis :D) 

But as I can see, the square brackets do offer a certain readability improvement in those cases mentioned above. Somehow though the [] I associate more strongly with 'indexing' and 'slices / arrays' whereas with the `()` I don't have a strong association with a single thing. They are already used for multiple parts, you get

```
func (params) (return_params)
x.(string)
```

and of course for grouping mathematical operations. 

I'll play around with the change and see if I get used to them as quickly :) 

Tao Liu

unread,
Jul 15, 2020, 10:59:13 AM7/15/20
to golang-nuts
This is similar to C++ template,  and it's very clear to understand.

kaleidawave

unread,
Jul 15, 2020, 10:59:13 AM7/15/20
to golang-nuts
Correct me if I am wrong but wouldn't square bracket syntax have an ambiguity around calling a function pointer at a position in a slice:

z := []func() int{x, y};
a := z[0]();

jpap

unread,
Jul 15, 2020, 10:59:19 AM7/15/20
to golang-nuts
Two thoughts:

1. How useful is it in practice to have separate start and end delimiters?  Does it matter for nested declarations?  I didn't see any nested declarations in the Draft Design document, for example.  If allow the same token on both sides, we could use the vertical bar, and not require the type keyword at all:

func f(T|int|)

struct{ T|int| }

interface{ T|int| }

[]T|int|{}

type S|T| struct { v T }


(A concrete example is included at the end of this e-mail.)

The above was one of the options Matt McCullough outlined in "Towards Clarity: Syntax Changes for Contracts in Go".

2. Otherwise, could we also consider double square brackets so it becomes immediately obvious that the expression has nothing to do with an array?  (The type keyword is not needed here either.)

func f(T[[int]])

struct{ T[[int]] }

interface{ T[[int]] }

[]T[[int]]{} 

type S[[T]] struct { v T }


(The same example in this style is included at the end of this e-mail.)


On the whole, I prefer [[...]] over |...| over [...] over (...), but would also prefer that type declarations stick out easily enough.

jpap


PS. An example from Matt Layher, rewritten using vertical bars:

// Package hashtable implements a basic hashtable for generic key/value pairs.
package hashtable

// A Table is a basic generic hashtable.
type Table|K comparable, V interface{}| struct {
    // hash is a function which can hash a key of type K with t.m.
    hash func(key K, m int) int

	m     int
	table [][]kv|K, V|
}

// A kv stores generic key/value data in a Table.
type kv|K, V| struct {
	Key   K
	Value V
}

// New creates a table with m internal buckets which uses the specified hash
// function for an input type K.
func New|K comparable, V interface{}|(m int, hash func(K, int) int) *Table|K, V| {
	return &Table|K, V|{
		hash:  hash,
        m:     m,
		table: make([][]kv|K, V|, m),
	}
}

PPS. The same example from Matt Layher, rewritten using [[...]]:

// Package hashtable implements a basic hashtable for generic key/value pairs.
package hashtable

// A Table is a basic generic hashtable.
type Table[[K comparable, V interface{}]] struct {
    // hash is a function which can hash a key of type K with t.m.
    hash func(key K, m int) int

	m     int
	table [][]kv[[K, V]]
}

// A kv stores generic key/value data in a Table.
type kv[[K, V]] struct {
	Key   K
	Value V
}

// New creates a table with m internal buckets which uses the specified hash
// function for an input type K.
func New[[K comparable, V interface{}]](m int, hash func(K, int) int) *Table[[K, V]] {
	return &Table[[K, V]]{
		hash:  hash,
        m:     m,
		table: make([][]kv[[K, V]], m),
	}
}

Yaw Boakye

unread,
Jul 15, 2020, 11:00:01 AM7/15/20
to Max, golang-nuts
I'm in favor of the square brackets. scala has them as well so we're not exactly blazing a new trail. I'm not in favor of a second `type` keyword whose benefit is to disambiguate syntax to the parser. we already use significant whitespace to identify tokens. for example, we expect to be parsing a function after we've parsed the `func\s+`. I'll take a parsing error over repeated the type keyword. In this regard we're not along either. Erlang's less-than-or-equals operator is =<. It inadvertently makes whitespace a requirement when doing binary syntax: `X = <<"hello">>`. Without the significant whitespace after the equals sign, the Erlang parser always finds the `=<` operator, which leads to a parsing error. A similar behavior for the square brackets syntax will be desirable.

--
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.
--
Curried programmer
I'm tweeting when I'm not coding when I'm not holding my niece.

rvi...@gmail.com

unread,
Jul 15, 2020, 12:31:03 PM7/15/20
to golang-nuts
Dear Developers:

You have spent several years on this and I'm impressed by the result, it looks beautiful!  I'm stunned.

As you can see from the recent spotlight shown on this syntax, in the meanwhile people have resorted to using unicode characters that sort of look like delimiters but are just part of the variable name, and then parsing them outside of Golang while the compiler doesn't realize the shenanigans that are going on with variable names that have Canadian Aboriginal Syllabics, resulting in a single variable name that looks like ImmutableTreeListᐸElementTᐳ but which contains no angle brackets.  (In fact here is what it looks like with angle brackets:ImmutableTreeList<ElementT> , a barely discernible difference.)

No one will soon forget this example.

I think that reflecting on the importance of visual distinction, the admirable step you have taken moving away from parentheses is not enough:

If [ and ] seem to work and can always be parsed unambiguously I would be very humbled and gracious if the designers could please show me me how the various examples and edge cases that have come up over the years would look if gofmt changed [ and ] in these places and, indeed, instead, [_ and _] were the standard format in these spots: 2 characters, an opening bracket followed by an underscore, and an underscore followed by a closing bracket.  (Which gofmt could convert these spots into.)

The suggestion is meant to resemble a blank spot someone has written in, as on a form.  The reason for letting gofmt convert it to that is to let people enter [ and ] at the keyboard, since you have done the hard work of making the spots unambiguous; this saves a character while entering at the keyboard, at the small expense of the formater removing a character from any identifier that happens to start or end with _ when used in that spot and using the short form syntax instead of the full gofmt syntax: in that case the formatter has no way of knowing that they meant to write the sloppy [ form for it to correct to [_ on its own.   (Since identifiers may, of course, both begin and end with an underscore themselves.)

Edge Cases Because Identifiers may begin or end with an undscore (or both)   

This allowance would in fact make _int_ wholly ambiguous: it could be a variable named _int_, or a type named int depending on where in the code it occurred. (If it occurred in an array index position it would refer to a variable.)

However, you have ensured that in fact the syntax itself makes these positions totally distinct even without the visual difference.   (And not many variables are being used which both begin and end in a _.)

Therefore at the spots where you have parsed [] as a generic, if someone were to enter [_int_] at the keyboard, it would be considered only as int - for something named _int you would have to enter [__int_] and for something named int_ you would have to enter [_int__], both of which seem pretty clear to me and an edge case that would not often come up.)

Supposing that the user were allowed to continue to enter [ and ] on the keyboard, since you have made the syntax unambiguous to the compiler in these spots, gofmt would turn them into [_ and _] wherever they refer to the generic syntax.

The constraint behind choosing [_ and _] is that it is visually distinct - always important for programmers - and unambiguous.  It is within the character set easily accessible by programmers and which Go already understands, and all Go programmers use ['s and _'s all the time.

Nobody looking at code with [_ _] can ever suppose it is anything other than a new syntax.

Look at the following and imagine that the [_ _] is like a form into which someone has entered with a typewriter:

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_]{}



I don't like different things to look the same.  [ and ] look the same as an array index.  They aren't an array index, so they shouldn't look like an array index.

But if you clever coders have made it so that it is unambiguous to the compiler when you mean to use the generic syntax, though you've written an opening or closing array limiter, then by all means let the programmer save having to search around for _ and allow them to use a bracket for gofmt to convert later.

The reference says:

Array types are always one-dimensional but may be composed to form multi-dimensional types.

[32]byte
[2*N] struct { x, y int32 }
[1000]*float64
[3][5]int
[2][2][2]float64  // same as [2]([2]([2]float64))

Look at that final example! [2][2][2]

I think that it would be unforutnately if some of those [ and ['s in some contexts can end up enclosing a type, and in other contexts enclose a value, depending on where you are in the parse tree, and for the programmer to never realize what is going on.

For all of these reasons I would greatly appreciate if you would show me all the possible edge cases you've discovered for types.  I would like to know whether the [_int_] notation would look beautiful for all of them.

To me it is orthogonal; uses the character set used by all versions of Go since the very beginning; breaks any Go version that doesn't know about them; looks easy on the eye; and can be checked for intent by the Go formatter, gofmt

How would it look at the edges? Please let me know by filling out this form: [__]

Seebs

unread,
Jul 15, 2020, 12:32:29 PM7/15/20
to golang-nuts
On Tue, 14 Jul 2020 23:19:40 -0700 (PDT)
Fino <xme...@gmail.com> wrote:

> I vote for <: and :>

I'm conflicted on it. <> match what several other languages do, but are
problematic for the parser. () is definitely overused. [] is tolerable.

I'd like to bring up the possibility of ??( and ??), mostly so that
whatever we end up with, people will be able to say "well, at least it
wasn't trigraphs".

-s

Dan Markham

unread,
Jul 15, 2020, 1:03:40 PM7/15/20
to golang-nuts
On Tuesday, July 14, 2020 at 9:45:41 PM UTC-7, robert engels wrote:
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). 

This really captures my feeling perfectly, the parser changes have to be less man hours to build and maintain going forward than the man hours of all the existing and new developers. All options have a downsides at least <T> is as "standard" as one can get when talking about  generics. 

Some version of <T> will help beginners in reducing the cognitive load when learning and using Go.  

Thanks,
-Dan 
 

 
> On Jul 14, 2020, at 11:13 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.
>
> To unsubscribe from this group and stop receiving emails from it, send an email to golan...@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.

Zippoxer

unread,
Jul 15, 2020, 1:40:56 PM7/15/20
to golang-nuts
Agreed. I've been a happy Gopher since 2011 and would really like to see <> or even [] instead of ().

Carla Pfaff

unread,
Jul 15, 2020, 1:52:03 PM7/15/20
to golang-nuts
I really like this square bracket syntax and I'm glad that Go does not repeat the mistakes other languages have made..

Ian Lance Taylor

unread,
Jul 15, 2020, 2:49:37 PM7/15/20
to robert engels, Ahmed (OneOfOne) W., golang-nuts
On Tue, Jul 14, 2020 at 9:45 PM robert engels <ren...@ix.netcom.com> wrote:
>
> 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).

Thanks for the note.

I'll briefly mention, as others have, that Scala successfully uses
square brackets for generics, and that in Go the map type already uses
square brackets to indicate the generic key type (though the value
type is outside the square brackets). So it's not like this is
entirely new ground.

It's also worth noting that the Go survey results
(https://blog.golang.org/survey2019-results) show us that the
languages most Go programmers are familiar with are Python and
JavaScript, which of course do not use generics at all with any sort
of brackets. So the familiarity argument only goes so far.

More seriously, though, let's look closely at Robert's example:

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

When parsing this without any information about what the identifiers
mean, this could be either an assignment of two values to two
variables:

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

or it could be a call of the instantiated function w<x, y> passing the
argument z and returning two results. By the way, don't get
distracted by the parentheses around z, it can be an arbitrary
expression, so although (z) might be unusual using parentheses around
a more complex expression might be entirely reasonable.

Without knowing the type of w, we can not parse this statement. This
has nothing to do with lookahead or the nature of parsing or anything
else. It's fundamental to the syntax.

In order for tools like gofmt and goimports to be effective and be
fast enough to run on file save, they have to be able to parse Go code
without knowing types. In the general case, where w might actually be
written as pkg.w, knowing the type of w will require chasing through
an arbitrary number of imported packages. Requiring simple tools like
gofmt and goimports to do this kind of type checking would break one
of the basic guidelines that Go has followed from the beginning: the
language must be easy and fast to parse without knowing types.

I'll note that C++ also has this syntactic problem, and indeed C++
cannot be parsed without full type information. This is part of why
tools that work with Go code run much faster than tools that work with
C++ code, and is part of why the Go compiler is much faster than
typical C++ compilers.

So it's really not a matter of "just fix the parser to use angle
brackets." As far as I've been able to tell, using angle brackets is
technically infeasible, given the way that Go works. Other languages
make different choices, and those choices work for them, but this is
not a choice that Go is able to make.

So, I hear you: you and many others would prefer angle brackets. But
unless and until someone explains how it could be done, we must
continue to regard that choice as off the table.

Sorry.

Ian

Hein Meling

unread,
Jul 15, 2020, 2:54:58 PM7/15/20
to golang-nuts
When you refer to beginners, I think you mean developers from other languages (Java/C#/C++...) that already know the concept of generics. I don't think they will have problems adapting to a square brackets notation... they are smart people, and after all they have to learn many other syntactical differences as well, so one more difference is not going to be the deal breaker. And if they make <mistakes>, the compiler will [surely] tell them ;-)

In fact, the square bracket notation is already adopted for declaring the type of maps and slices. So switching to angle brackets will make Go's generics implementation internally inconsistent, since you must declare a map[string]int with angle brackets and then elsewhere, you will be using <int> for the same concept. I think it is much more important that Go remains internally consistent in its use of angle brackets.

Best,
:) Hein

Hein Meling

unread,
Jul 15, 2020, 3:02:25 PM7/15/20
to golang-nuts
Correction: I think it is much more important that Go remains internally consistent in its use of "square" brackets.

Ian Lance Taylor

unread,
Jul 15, 2020, 3:27:00 PM7/15/20
to Paul Johnston, golang-nuts
On Tue, Jul 14, 2020 at 10:06 PM Paul Johnston <pcj...@gmail.com> wrote:
>
> 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

Well, you need to consider not just the definitions, but also the uses.

The suggested syntax, whether with parentheses or square brackets, has
what I consider to be a very nice property: the definition and use
syntaxes are very similar. For the definition we write

func F[type T]() {}

For the use we write

F[int]()

The two aren't identical, but the syntax is parallel.

The same is true in Java. Admittedly in C++ the syntaxes are not
parallel, and are more like what you suggest.

Ian

Seebs

unread,
Jul 15, 2020, 3:59:38 PM7/15/20
to golang-nuts
On Wed, 15 Jul 2020 12:26:15 -0700
Ian Lance Taylor <ia...@golang.org> wrote:

> The suggested syntax, whether with parentheses or square brackets, has
> what I consider to be a very nice property: the definition and use
> syntaxes are very similar. For the definition we write
>
> func F[type T]() {}
>
> For the use we write
>
> F[int]()
>
> The two aren't identical, but the syntax is parallel.

Maybe the "type" should be required in both? That feels annoying and
ugly, but it would eliminate any possible ambiguity about how to
interpret the name in the []:

var F1 []func()
F1[otherpkg.Y]()
func F2[type T]() {}
F2[otherpkg.Z]()

-s

David Riley

unread,
Jul 15, 2020, 4:21:38 PM7/15/20
to Ian Lance Taylor, robert engels, Ahmed (OneOfOne) W., golang-nuts
On Jul 15, 2020, at 2:48 PM, Ian Lance Taylor <ia...@golang.org> wrote:
>
> More seriously, though, let's look closely at Robert's example:
>
> a, b = w < x, y > (z)

TBH, I think a big argument in favor of square brackets over angle brackets is that they ascend above the center line in most typefaces, which makes them much more visually distinct from the shorter operators and many lowercase letters. This is similar to parens and curly braces while still being visually distinct from both.

As something that is essentially a metaparameter which is similar to, but distinct in function to the arguments in parens, there's a lot of good consistency going on there that I personally find easier to visually scan without syntax highlighting than I do angle brackets. But that could be just me, and it does depend on typeface, so take it with whatever grains of salt you need to.


- Dave


Ian Lance Taylor

unread,
Jul 15, 2020, 4:50:27 PM7/15/20
to Bakul Shah, gri, golang-nuts
On Tue, Jul 14, 2020 at 11:06 PM Bakul Shah <ba...@iitbombay.org> wrote:
>
> 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).


Thanks for the note. It's an interesting idea, but I'm not quite sure
it works to have prefix types with no real delimiters. It may work to
write "int pair pair" but does it work to write "func() int pair
pair"? That is the type of a pair of functions where each function
returns the type "int pair". If we have to start carefully deciding
where we need to add parentheses then I'm not sure we've gained very
much by switching to this kind of syntax.

Ian

Ian Lance Taylor

unread,
Jul 15, 2020, 5:00:29 PM7/15/20
to Jan Mercl, Robert Griesemer, golang-nuts
On Wed, Jul 15, 2020 at 5:44 AM Jan Mercl <0xj...@gmail.com> wrote:
>
> My 2c - Alternative type parameters syntax (ab)using @$:
> https://docs.google.com/document/d/1AoU23DcNxYX2vYT20V2K16Jzl7SP9taRRhIT8l_pZss/edit?usp=sharing

Thanks for the detailed writeup. I'm a bit concerned about name
scoping. Also about the fact that constraints can apparently show up
anywhere; is that always going to work? And I don't think that
Vector@$T is going to be all that intuitive for anybody to read.

Ian

Ian Lance Taylor

unread,
Jul 15, 2020, 5:02:51 PM7/15/20
to jpap, golang-nuts
On Wed, Jul 15, 2020 at 7:59 AM jpap <jpa...@gmail.com> wrote:
>
> Two thoughts:
>
> 1. How useful is it in practice to have separate start and end delimiters? Does it matter for nested declarations? I didn't see any nested declarations in the Draft Design document, for example. If allow the same token on both sides, we could use the vertical bar, and not require the type keyword at all:
>
> func f(T|int|)
>
> struct{ T|int| }
>
> interface{ T|int| }
>
> []T|int|{}
>
> type S|T| struct { v T }

I think that vertical bars will have some of the same problems as
angle brackets, since the vertical bar is itself an operator in Go.

a, b = w | x, y | (z)

Ian

Ian Lance Taylor

unread,
Jul 15, 2020, 5:05:35 PM7/15/20
to kaleidawave, golang-nuts
On Wed, Jul 15, 2020 at 7:59 AM kaleidawave <kalei...@gmail.com> wrote:
>
> Correct me if I am wrong but wouldn't square bracket syntax have an ambiguity around calling a function pointer at a position in a slice:
>
> z := []func() int{x, y};
> a := z[0]();

A parsing ambiguity arises when the same code can be parsed in
different ways. This case isn't an ambiguity, because it is always
parsed as "z[0]" followed by "()". It's true that until type checking
we don't know whether "z[0]" is an index expression or a function
instantiation. But that is OK. We have similar issues in the
language today for "f(x)", which could be either a function call or a
type conversion.

Ian

Randall O'Reilly

unread,
Jul 15, 2020, 5:37:01 PM7/15/20
to Ian Lance Taylor, robert engels, Ahmed (OneOfOne) W., golang-nuts
Regarding the parsing: as I suggested in my email, but perhaps was unclear, in case of two conflicting *purely syntactic* interpretations (without using any type information), you could just choose the more high-frequency interpretation (i.e., type parameters) and force the programmer to use additional parens if they actually want the other interpretation.

So in the case of the example, w < x, y > (z) is *automatically, syntactically* interpreted as a function call using 2 type parameters, and if you want it to be two different relational expressions, you have to wrap them in parens: (w < x), (y > (z))

This latter use is likely to be vanishingly rare (someone could run their code analysis on it), so this seems like a small inconvenience. Maybe there are other such conflicts? If this is the only one, I don't think it is sufficient to rule out the use of < >.

My mention of the point about gofmt was just that it should get rid of the seemingly redundant extra paren around (z), but I just tried it and apparently it does not, even with gofmt -s. Anyway, the above point stands: you CAN parse < > correctly by just making purely syntactic choices in the case of conflicts, and this applies to all Go tools using the same purely syntactic parsing pass.

Also, according to Tiobe, Scala is ranked 39, and whereas C, Java, C++, C#, are all top 10, and the C family is clearly the syntactic heritage of Go.

And the use of [ ] in map is more semantically associated with its conventional use as an element accessor in arrays / slices, not with some more general kind of type parameter.

- Randy
> --
> 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/CAOyqgcXJAXsPNQLp1Jwqp%2B_U95wQCXk_qdhK2MUQmhEyLUUWHQ%40mail.gmail.com.

Robert Engels

unread,
Jul 15, 2020, 5:37:02 PM7/15/20
to Nic Long, golang-nuts
But Scala doesn’t use [] for anything else - Go does. 

On Jul 15, 2020, at 9:58 AM, 'Nic Long' via golang-nuts <golan...@googlegroups.com> wrote:

Square brackets work great. Scala uses them for generics to great effect. They also make me think of Clojure, which introduced them to Lisp for similar reasons to here - to avoid too much overload of regular parentheses - and which people have been very happy with.
--
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.

Robert Engels

unread,
Jul 15, 2020, 5:42:02 PM7/15/20
to Randall O'Reilly, Ian Lance Taylor, Ahmed (OneOfOne) W., golang-nuts
This is what I was thinking. Format for the common case. At compile/link time you would be able to figure out the conflict and throw an error and the dev could add the parens to make it unambiguous.

> On Jul 15, 2020, at 4:36 PM, Randall O'Reilly <rcore...@gmail.com> wrote:
>
> Regarding the parsing: as I suggested in my email, but perhaps was unclear, in case of two conflicting *purely syntactic* interpretations (without using any type information), you could just choose the more high-frequency interpretation (i.e., type parameters) and force the programmer to use additional parens if they actually want the other interpretation.

Ian Lance Taylor

unread,
Jul 15, 2020, 5:47:44 PM7/15/20
to Randall O'Reilly, robert engels, Ahmed (OneOfOne) W., golang-nuts
On Wed, Jul 15, 2020 at 2:36 PM Randall O'Reilly <rcore...@gmail.com> wrote:
>
> Regarding the parsing: as I suggested in my email, but perhaps was unclear, in case of two conflicting *purely syntactic* interpretations (without using any type information), you could just choose the more high-frequency interpretation (i.e., type parameters) and force the programmer to use additional parens if they actually want the other interpretation.
>
> So in the case of the example, w < x, y > (z) is *automatically, syntactically* interpreted as a function call using 2 type parameters, and if you want it to be two different relational expressions, you have to wrap them in parens: (w < x), (y > (z))
>
> This latter use is likely to be vanishingly rare (someone could run their code analysis on it), so this seems like a small inconvenience. Maybe there are other such conflicts? If this is the only one, I don't think it is sufficient to rule out the use of < >.
>
> My mention of the point about gofmt was just that it should get rid of the seemingly redundant extra paren around (z), but I just tried it and apparently it does not, even with gofmt -s. Anyway, the above point stands: you CAN parse < > correctly by just making purely syntactic choices in the case of conflicts, and this applies to all Go tools using the same purely syntactic parsing pass.

That's true. We could do that. It's not backward compatible, and it
doesn't fit the Go 2 transitions plan, but we could do it if we really
had to.

But we don't really have to. We can use square brackets instead, or
we can go back to using parentheses. This thread suggests that while
clearly some people prefer angle brackets, it is not a dominant
consensus.

Ian

jimmy frasche

unread,
Jul 15, 2020, 5:51:59 PM7/15/20
to Ian Lance Taylor, Randall O'Reilly, robert engels, Ahmed (OneOfOne) W., golang-nuts
I didn't care about () except for having to then have extra parens
around types in a lot of places which was very annoying and came up
often. If [] fixes that, great!
> --
> 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/CAOyqgcVo3d4zrXkGtxPzMUSC%2BJ0ju_B3DsQZj%2B%3D5Ec2%2B3y0%3DSA%40mail.gmail.com.

Dan Kortschak

unread,
Jul 15, 2020, 6:04:31 PM7/15/20
to golan...@googlegroups.com
On Wed, 2020-07-15 at 14:36 -0700, Randall O'Reilly wrote:
> And the use of [ ] in map is more semantically associated with its
> conventional use as an element accessor in arrays / slices, not with
> some more general kind of type parameter.

The [] syntax can be viewed as an indexing operation perfectly well in
the context of generics; a generic function or type is a map of
implementations or types and so the [] syntax is a type index into that
map, just as map[T1]T2 is a type index into the builtin generic map
type. This is not mental stretch.


Seebs

unread,
Jul 15, 2020, 6:11:01 PM7/15/20
to golang-nuts
On Wed, 15 Jul 2020 14:51:17 -0700
jimmy frasche <soapbo...@gmail.com> wrote:

> I didn't care about () except for having to then have extra parens
> around types in a lot of places which was very annoying and came up
> often. If [] fixes that, great!

I was pretty unhappy with () just because there's too many () and it's
hard to tell them apart by eyeball. Even if the parsing isn't strictly
ambiguous, it's annoying to try to decipher; I already feel like too
many things use ()s.

That said, if people don't like square brackets, I'm totally prepared
to make worse suggestions until they give up and say square brackets
aren't that bad.

old proposal:
func F(type T) (x T) (T, error)

new proposal:
func F[type T] (x T) (T, error)

:seebsno:
func F/type T/(x T) (T, error)

Or we could do sed-style "any character can be the delimiter if
you use it where we expect a delimiter", except using "right before
the type keyword", so for instance:
func F!type T!(x T) (T, error)
func Fxtype Tx(x T) (T, error) // yes you can use x

And we could also support regex-style flags on these to resolve
the "how should the compiler actually implement this", by letting you
specify implementation-choice flags:
func F/type T/g(x T) (T, error) // generate specialized code
func F/type T/i(x T) (T, error) // inline
func F/type T/p(x T) (T, error) // print lines to output??!
func F/type T/q(x T) (T, error) // exit compilation early

Not bad enough? No problem! We'll just... use XML. That should help us
win over the enterprise market, right?
<generic implementation="generated,refected">
<type T constraints="<![CDATA[Stringer]]>">
func F(x T) (T, error)
</type>
</generic>

(The indentation is mandatory, of course, with two spaces before the
<type> tags, and a tab with no leading spaces before the code inside
them.)

If it weren't for the parser things, I'd prefer <> to []. However, I am
also fine with [type T]; it's clear and unambiguous, and gets away from
the "everything goes in ()s" that was my only real complaint about the
proposal.

(And yes I thought of the "unicode characters that happen to look like
angle brackets" idea, but someone already implemented it that way, so
it's not novel.)

-s

Yaw Boakye

unread,
Jul 15, 2020, 6:14:20 PM7/15/20
to Dan Kortschak, golan...@googlegroups.com
To summarize the thread so far:

- parenthesis is a no-no
- angle brackets are popular/common but that alone doesn't make good argument. some languages have already deviated. scala is popular, uses square instead of angle brackets.
- go's current pseudo-generic data structure, the map, already uses the square bracket (e.g. map[int]int), a vote for square brackets. internal syntax consistency is key.

--
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.

Dan Kortschak

unread,
Jul 15, 2020, 6:27:55 PM7/15/20
to golan...@googlegroups.com
On Wed, 2020-07-15 at 17:10 -0500, Seebs wrote:
> That said, if people don't like square brackets, I'm totally prepared
> to make worse suggestions until they give up and say square brackets
> aren't that bad.
>

Let's bring back the special five keywords!

func despiteallobjections F insofaras type T thetruthofthematter (x T) (T, error)

There is no parsing ambiguity, the syntax is already familiar to
Gophers and it clearly indicates the intention and the difficulty of
coming up with a sensible generics declaration syntax. This is win win
win!


Bakul Shah

unread,
Jul 15, 2020, 6:28:09 PM7/15/20
to Ian Lance Taylor, gri, golang-nuts
The syntax rule I envisioned was this:
type: ... | type generic-type | ...

To me, using full parenthesization, this would be
func() int pair pair" == "((func() int) pair)pair --(1)
What you seem to want is
(func() (int pair)) pair --(2)

Contrast that with
pair[pair[func() int]] --(1)
and
pair[func()pair[int]] --(2)

As I see it, the alternate syntax is no worse for extreme cases but much better for the common case.

The second issue is the usage for generic functions. Contrast

(float64, int)PowN(1.2, 3)
with
PowN[float64, int](1.2, 3)
or currently
PowN(float64, int)(1.2, 3)

I find the alternate syntax easier because conceptually the concrete types  used to select a specific concrete function is a different activity than passing arguments to a function at runtime so IMHO they should be visually different.  It is almost like  C style cast. Think of it as casting a generic function (or type) to a concrete function or type!


Dan Kortschak

unread,
Jul 15, 2020, 6:36:46 PM7/15/20
to golan...@googlegroups.com
On Wed, 2020-07-15 at 15:27 -0700, Bakul Shah wrote:
> The second issue is the usage for generic functions. Contrast
>
> (float64, int)PowN(1.2, 3)
> with
> PowN[float64, int](1.2, 3)
> or currently
> PowN(float64, int)(1.2, 3)
>
> I find the alternate syntax easier because conceptually the concrete
> types used to select a specific concrete function is a different
> activity than passing arguments to a function at runtime so IMHO they
> should be visually different. It is almost like C style cast. Think
> of it as casting a generic function (or type) to a concrete function
> or type!

This is going to be a fairly individual thing though, I find
PowN[float64, int](1.2, 3) much easier to read.


Ian Lance Taylor

unread,
Jul 15, 2020, 7:11:06 PM7/15/20
to Bakul Shah, gri, golang-nuts
On Wed, Jul 15, 2020 at 3:27 PM Bakul Shah <ba...@iitbombay.org> wrote:
>
> The syntax rule I envisioned was this:
> type: ... | type generic-type | ...
>
> To me, using full parenthesization, this would be
> func() int pair pair" == "((func() int) pair)pair --(1)
> What you seem to want is
> (func() (int pair)) pair --(2)
>
> Contrast that with
> pair[pair[func() int]] --(1)
> and
> pair[func()pair[int]] --(2)
>
> As I see it, the alternate syntax is no worse for extreme cases but much better for the common case.

What I was trying to get at wasn't really whether the syntax is better
or worse when parenthesized correctly. It was about how it reads
without parentheses, and how people will know when to add parentheses.
With the square brackets it seems clear to me what the cases mean and
how to write them. With the prefix notation it seems less clear.



> The second issue is the usage for generic functions. Contrast
>
> (float64, int)PowN(1.2, 3)
> with
> PowN[float64, int](1.2, 3)
> or currently
> PowN(float64, int)(1.2, 3)
>
> I find the alternate syntax easier because conceptually the concrete types used to select a specific concrete function is a different activity than passing arguments to a function at runtime so IMHO they should be visually different. It is almost like C style cast. Think of it as casting a generic function (or type) to a concrete function or type!

Personally I find it more natural to think of the types as arguments.
And that is how most popular languages handle them too. I understand
that you feel differently.

Ian

Denis Cheremisov

unread,
Jul 15, 2020, 8:16:21 PM7/15/20
to golang-nuts
The big argument I always found less and greater signs to be not visually distinctive enough, at least with fonts I am using. [ and ] are larger and I was really happy with them in Scala although I dislike the language in general. 
Seriously though, the real big argument is [] are already used with in two of three generic types ([]T, map[K]V, only is chan T is different). And there's a huge argument, with the performance of simple tools like gofmt, etc.

среда, 15 июля 2020 г. в 23:21:38 UTC+3, frave...@gmail.com:

lotus.devel...@gmail.com

unread,
Jul 15, 2020, 10:28:22 PM7/15/20
to golang-nuts
Personally think this way is easier to read. In this way, I wrote a few examples, which may be different from the creator

* For example: define a node
type<T> Node struct{
    prev *Node<T>
    data T //data *T
    next *Node<T>
}
* Call request
var node Node<int>

* Let's take another example, such as the maximum value
* What to do when different types require the maximum value
type maxIntCallback func(one,two int) bool
type maxFloat64Callback func(one,two float64 /*float32*/) bool
//...other types

//If you want to use it, such as a certain function. func intCompare(one,two int,max Max) int
//1.
one,two := 10,20
max := Compare(one,two,_Max)
fmt.Println(max)


func _Max(one,two int) int {
   max := one
   if max > two{
       return max
   }
   return two
}
func intCompare(one,two int,max Max) int {
   return max(one,two)
}

//2.
one,two := 10,20
   var max = Compare(one,two, func(one, two int) int {
       max := one
       if max > two{
           return max
       }
       return two
})

* Although the above example can also be done using interfaces, it's like all parameters in Java use Object as the type, so it is always bad.

* Here is a generic example

type<T> maxCallback func(one,two T) T
func<T> compare(one,two T,call maxCallback<T>) T {
   return call(one,two)
}

//int
one,two := 10,20
compare<int>(one,two,func(one,two int ) int {
   //...
})

//float
one,two := 10.0,20.0
compare<float64>(one,two,func(one,two float64 ) float64 {
   //...
})

* In this way, it is easier to read and people-oriented



在 2020年7月15日星期三 UTC+8下午1:06:24,Paul Johnston写道:
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



On Tuesday, July 14, 2020 at 10:45:41 PM UTC-6 ren...@ix.netcom.com wrote:
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.

Hoy

unread,
Jul 15, 2020, 10:28:22 PM7/15/20
to golang-nuts
Hi,

Why do we have to allow people to write something like `w<x,y>(z)` or `w(x,y)(z)` or `w[x,y](z)`? It's not "go-style" stuff.
Something more in "go-style" should be:


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


It is much more clear and elegant, just like what we have for error handling.
In this way, brackets would not be narrowed in the same single line, thus being friendly to read.
And we would not meet the ambiguity problem.

在 2020年7月15日星期三 UTC+8上午5:56:01,gri写道:

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.

Randall Oreilly

unread,
Jul 15, 2020, 10:28:25 PM7/15/20
to Yaw Boakye, Dan Kortschak, golang-nuts
Perhaps another source of resistance to < > is the trauma many of us have from its major obfuscation of C++ code?

- Randy
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAPJoGXsM%2BuzfS_2sNBXP2d4z%3DvC-2gHOu_QS%3DL3PSQKkx6_drg%40mail.gmail.com.

Dan Kortschak

unread,
Jul 15, 2020, 10:51:23 PM7/15/20
to golang-nuts
On Wed, 2020-07-15 at 18:09 -0700, lotus.devel...@gmail.com
wrote:
> Personally think this way is easier to read. In this way, I wrote a
> few examples, which may be different from the creator

Please post in unstyled plain text. It is almost impossible to read the
code examples you have there in dark text on a black background.


Kaveh Shahbazian

unread,
Jul 16, 2020, 12:42:47 AM7/16/20
to golang-nuts
Proposals that employ the type keyword, look more Go-ish. Go is already doing many things differently than other PLs in C-ish family - the if without parentheses and no semicolon are two of things that hit most when switching frequently between Go and another PL.

func SomeContext() {
    var aSlice [](typeㅤ T)
    var aMap map[typeㅤ T](type ㅤU)
    var aChannel <-chan (typeㅤ T)

    aSlice = make([]int, 0)
    aMap = make(map[int]int)
    aChannel = make(chan int)

    otherStuff(aSlice, aMap, aChannel)
}

func Action(x typeㅤ T, y, delta type ㅤU) (xres type ㅤT, yres type ㅤU) {
    return
}

type Data struct {
    typeㅤ T
    type ㅤU

    Input  T
    Output U
}

type Processor interface {
    Process(typeㅤ T) (Data {type ㅤT = T, typeㅤ U = string}, error)
}

type Actual struct {
    type ㅤR
}

func (Actual) Process(input typeㅤ T = R) (Data{typeㅤ T = T, type ㅤU = string}, error) {
    return Data { input, "" }
}

Henrik Johansson

unread,
Jul 16, 2020, 3:16:51 AM7/16/20
to Dan Kortschak, golang-nuts
This is good. I have been reaching for a consistency check and this just may be it.

--
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.

Jan Mercl

unread,
Jul 16, 2020, 8:23:17 AM7/16/20
to Robert Griesemer, golang-nuts
Resending to the list after mistakenly replied off list. My apologies.

----

On Wed, Jul 15, 2020 at 10:59 PM Ian Lance Taylor <ia...@golang.org> wrote:

> Thanks for the detailed writeup.

It might have been too detailed and suffered from low information
density. Let me amend it with a tl;dr:

----

1. The main idea is to remove the parenthesized type list while
keeping exactly 100% of the semantics of the original draft.

2. $T declares a type parameter. (For the mnemotechnical clue cf. the
POSIX shell env vars or Tcl vars, to name a few.)

3. U@V, u-at-v, instantiates generic type U using type argument V.
(cf. value instance at address, now type-parameter instantiated at
type)

4. Instantiating a generic type W with a type parameter X that is
declared in the same place means plugging 2 into 3, hence W@$X.

----

The particular syntax is less important here. But it seems to remove
the parsing ambiguities of the original draft.

Motivational example of a function signature:

Original:

func Keys(type K comparable, V interface{})(m map[K]V) []K

Alternative:

func Keys(m map[$K comparable]$V) []K

----

> I'm a bit concerned about name
> scoping.

Then I must have miscommunicated something. The intent is to have the
type parameters live in the exact same scope as in the original draft.
The comments about $T and T in the same scope just tried to say that
there cannot be both a type parameter $T and other named, but
different thing T in the same scope. Exactly as in the original, but
there the $ sign does not exist, so there's nothing to talk about in
this regard.

> Also about the fact that constraints can apparently show up
> anywhere; is that always going to work?

In the original draft a type parameter constraint appears only once,
next to the type parameter declaration. In the alternative one is the
same, only once, next to the type parameter declaration. Hence I
expect the alternative version to work in exactly the same way.

> And I don't think that
> Vector@$T is going to be all that intuitive for anybody to read.

This is subjective by definition, but I'd like to point out something
different. We write and read code not by intuition but by using quite
precise knowledge about the grammar and semantics of the language.

Intuition can of course help us to learn that necessary level of
knowledge to become a fluent language user.

f(x), for example, is intuitively a function call, because we write
f(x) in math classes in school.

a+b is intuitively an addition, because that's how we write it in math.

a*b is not very intuitive, because we write multiplication in math as
ab, a.b, axb. (The last has at least some visual resemblance to the
asterisk, admitted.)

Okay, now that we know the correct multiplication symbol, what an
unary multiplication is intuitively supposed to mean and why it
actually denotes dereferencing a pointer or pointer-ness of a type?

a^b is intuitively exponentiation, because that's how we write it in
math. But it's actually a xor b in Go.

The conclusion is, sometimes intuition helps in learning the language,
other times it can hurt. The only thing that matters at the end of the
day is knowing the language and that's no more connected much with
intuition.

Axel Wagner

unread,
Jul 16, 2020, 9:13:08 AM7/16/20
to golang-nuts
I dislike the idea of using $ and @ because I don't want to add new symbols to the language, if it can be avoided. In general I'd argue that Go tends to use keywords instead of symbols to convey meaning - e.g. we don't write `a -> b`, but `func(a) b`. There are exceptions (pointers are a big one) of course, but Go is still pretty light on symbols.
I think the overuse of symbols to convey semantics is a huge reason that I find perl and Haskell so unreadable, personally.

--
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.

Jan Mercl

unread,
Jul 16, 2020, 9:39:15 AM7/16/20
to Axel Wagner, golang-nuts
On Thu, Jul 16, 2020 at 3:12 PM 'Axel Wagner' via golang-nuts
<golan...@googlegroups.com> wrote:

> I dislike the idea of using $ and @ because I don't want to add new symbols to the language, if it can be avoided. In general I'd argue that Go tends to use keywords instead of symbols to convey meaning - e.g. we don't write `a -> b`, but `func(a) b`. There are exceptions (pointers are a big one) of course, but Go is still pretty light on symbols.
> I think the overuse of symbols to convey semantics is a huge reason that I find perl and Haskell so unreadable, personally.

There are 31 printable, non blank, non-digit, non-letter ASCII
symbols. Go (lexical grammar) currently uses 27 of them. I'm not sure
if using 27 of 31 vs using 29 out 31 makes a notable difference. (I
have not counted smybol groups like ":=" ">=", "<-" etc., but that
makes the number of symbols used in Go only bigger, not smaller.)

But of course, a keyword would work as well. For example, $ can be
replaced with no semantic change with 'type'. And @ can be dropped
while keeping the original draft syntax for type instantiation - and
the original parsing ambiguities. Then we can have something like

Original:

func Keys(type K comparable, V interface{})(m map[K]V) []K { ... }
s := Keys(int, string)(m)

Alternative-AW:

func Keys(m map[type K comparable]type V) []K
s := Keys(int, string)(m)

FTR: In the alternative draft I missed the `~` (tilde) character, so
there are not 3 but actually 4 ASCII symbols not used by Go.

But I like the $, @ option because the language change is pushed
"down" to be based mainly in the lexer grammar. I haven't made any
attempt to write a parser for the alternative draft, but I think this
makes the changes on top of the existing parser pretty minimal. The
original draft, on the other hand, needs IMO much more changes.

gcf

unread,
Jul 16, 2020, 12:47:22 PM7/16/20
to golang-nuts
Its worth noting that Rust does something similar with the "turbofish" operator in expressions:

"42".parse::<i32>()

I personally think this works quite well.

On Wednesday, July 15, 2020 at 5:41:51 AM UTC-7, Michal Strba wrote:
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.

Kulics Wu

unread,
Jul 16, 2020, 12:47:27 PM7/16/20
to golang-nuts

Why not try the design of Feel language,
There is no ambiguity and no more code.
```
type [Item T] struct {     
    Value T 
func (it [Item T]) Print() {     
    println(it.Value) 
func [TestGenerics T V]() {     
    var a = [Item T]{}     
    a.Print()     
    var b = [Item V]{}     
    b.Print()
 } 
func main() {    
     [TestGenerics int string]() 
}
```

Alon Han

unread,
Jul 16, 2020, 12:47:29 PM7/16/20
to golang-nuts
 `generics`  looks like  a space ,
 
like c++  namespace/classSpace use `::`  `.`  `->`
and `::`  is not used in golang.


```
type A::(x,y,z) struct{ }
func f::(x,y,z)()(){}
interface  B::(x,y,z)()(){}
[]S::(x,y,z){}
```
`FOO::(x,y,z)`  just like a   `string`  for  naming .
and  the args x,y,z  are  defined without  types.

joshua harr

unread,
Jul 16, 2020, 3:41:49 PM7/16/20
to golang-nuts
Just a note on your rationale for why not to use <: :> :
"... requires more typing." Golang has, rather famously, never shied away from making developers type more. The reason it hasn't, as far as I understand, is that code is read far often than it is written, and so the extra verbosity is worth the ease in reading the code. IMHO, that principle very much applies here. The *readability* of the syntax should be a far more important consideration than whether there is an extra character in the syntax.

Ian Lance Taylor

unread,
Jul 16, 2020, 3:51:03 PM7/16/20
to joshua harr, golang-nuts
On Thu, Jul 16, 2020 at 12:41 PM joshua harr <joshu...@gmail.com> wrote:
>
> Just a note on your rationale for why not to use <: :> :
> "... requires more typing." Golang has, rather famously, never shied away from making developers type more. The reason it hasn't, as far as I understand, is that code is read far often than it is written, and so the extra verbosity is worth the ease in reading the code. IMHO, that principle very much applies here. The *readability* of the syntax should be a far more important consideration than whether there is an extra character in the syntax.

That's a fair point. Having two characters is still in my mind a
disadvantage, but I agree that that disadvantage could be outweighed
by a gain in readability.

That said, personally I don't find <:T:> to be any more (or less)
readable than [T].

Ian
> --
> 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/985c8685-484c-417c-a421-fe7a222d56c7n%40googlegroups.com.

Ahmed Mones

unread,
Jul 16, 2020, 4:01:10 PM7/16/20
to Ian Lance Taylor, joshua harr, golang-nuts
Why not have a github emoji vote.

1. () `type MyMap(K comparable, V something) struct { m map[k]v }; fn(int, float)(4, .20);`
2. [] `type MyMap[K comparable, V something] struct { m map[k]v }; fn[int, float](4, .20);`
3. <::> `type MyMap<:K comparable, V something:> struct { m map[k]v }; fn<:int,float:>(4, .20);`
4. ::<> `type MyMap::<K comparable, V something> struct { m map[k]v }; fn::<int,float>(4, .20);`
5..8 ... other viable suggestions

That way the community would be able to easily vote and give the developers a better idea,
because a lot of people don't even use the mailing list.




You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/7t-Q2vt60J8/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAOyqgcUu0Jh%3DCsQ-0zy%3DkY3uFesYPBUAovYbvUyvPxpW%2BpPEiw%40mail.gmail.com.

jpap

unread,
Jul 16, 2020, 4:12:17 PM7/16/20
to golang-nuts
On Thursday, 16 July 2020 at 12:51:03 UTC-7 Ian Lance Taylor wrote:
On Thu, Jul 16, 2020 at 12:41 PM joshua harr <joshu...@gmail.com> wrote:
> Just a note on your rationale for why not to use <: :> :
> "... requires more typing." Golang has, rather famously, never shied away from making developers type more. The reason it hasn't, as far as I understand, is that code is read far often than it is written, and so the extra verbosity is worth the ease in reading the code. IMHO, that principle very much applies here. The *readability* of the syntax should be a far more important consideration than whether there is an extra character in the syntax.

That's a fair point. Having two characters is still in my mind a
disadvantage, but I agree that that disadvantage could be outweighed
by a gain in readability.

That said, personally I don't find <:T:> to be any more (or less)
readable than [T].

Ian

I agree that readability should be a big consideration.

The problem with [T] is that you need to actively read the surrounding code to discern between a possible array access or type parameter.  I would prefer it to be immediately obvious so that visually scanning through code is as fast and clear as can be.

An alternative syntax to [T] that could enable "quick looks" is [[T]].  It would be nice to use <<T>>, but that has parsing ambiguities with the existing bit-shift operator, just as <T> is ambiguous with the comparison operator.

The burden of typing the same character twice on a keyboard in quick succession for [[T]] is more or less the same as the single-character version [T] and less so than two-character delimiters like <: and :>, and especially those delimiters that require the shift-key in play at the same time.

jpap

Carla Pfaff

unread,
Jul 16, 2020, 4:19:10 PM7/16/20
to golang-nuts
Generics are not the most important part of the language/code. Let's not make them stick out like a sore thumb.

jpap

unread,
Jul 16, 2020, 4:44:21 PM7/16/20
to golang-nuts
Notwithstanding a concise unambiguous alternative, I would rather type parameters "stick out" so they are easily identified when visually scanning through code.  I read a lot more code than I write, even as I write Go full-time.

Here are two playgrounds (that don't format/run) comparing a generic hash table implementation using [[T]] vs. [T], based on a code sample by Matt Layher.  I don't think [[T]] is offensive to the surrounding code, and would love to know what more people think.  I'm happy to see generics progressing in Go and can't wait to use it either way.


jpap

Carla Pfaff

unread,
Jul 16, 2020, 4:53:53 PM7/16/20
to golang-nuts
On Thursday, 16 July 2020 at 22:44:21 UTC+2 jpap wrote:
I don't think [[T]] is offensive to the surrounding code, and would love to know what more people think.

It becomes even worse once you nest generic types:

    MyMap[[string, MyList[[int]]]]
 

Robert Viragh

unread,
Jul 16, 2020, 5:44:11 PM7/16/20
to golan...@googlegroups.com
Hi guys,

Great conversation! I think that seeing a lot of edge cases would make it easiest to see what is best.

Since the conversation went into [[ and ]]

Before coming up with [_ and _] I very strongly considered [[ and ]] because I thought things should be orthogonal and different things should look different, not the same, and I thought that looked pretty different.

As I recall, I didn't like the look of it, and especially I didn't like that it seemed to be nesting something.  However I did recall that python has """ (triple quotation mark to start a multiline string) which is like a special kind of quotation mark, yet consists of repeating the character, so I very strongly considered [[ and ]] or even [[[ and ]]] as the right solution.  I just didn't think it looked that great.

However to me the few examples given so far look pretty good with [_ and _] instead of [[ and ]].  Any more corner cases?  keep them coming!

By jpap:
Here it is with [_ and _] (doesn't run, obviously):

I find this extremely readable, like crazy readable if you remember that [_ _] is a mnemonic for fill in the blank.

By Carla Pfaff:

>MyMap[[string, MyList[[int]]]]

Here it is with [_ and _]:
MyMap[_string, MyList[_int_]_]

that's really obvious to me if you consider that you're filling in the blank - MyList is a generic and you're "filling the blank" in with int, and MyMap is a generic and you're "filling the blank" in with string and MyList.  I can't parse  MyMap[[string, MyList[[int]]]] with my eyes even if I know what it's supposed to mean - I can't even see if the brackets are matched or unmatched, can you?

Could people reply with more corner examples?  I think a large wealth of cases would best show the different options.

The team came up with a really clever way to let [ and ] itself be unambiguous (this whole thread, starting at the top), so I do think making it more explicit (turning those [ and ] into [_ and _] at the appropriate spots) could just be handled by gofmt.  This would make the gofmt'ed output very readable forever and the user could still just enter [ and ] at the keyboard.

Is there some collection of edge cases?  Can people come up with some more?

I think we should see how it looks especially at the corners and more cases and examples would show that best.

burak serdar

unread,
Jul 16, 2020, 5:51:23 PM7/16/20
to Robert Viragh, golang-nuts
On Thu, Jul 16, 2020 at 3:43 PM Robert Viragh <rvi...@gmail.com> wrote:
>
> Hi guys,
>
> Great conversation! I think that seeing a lot of edge cases would make it easiest to see what is best.
>
> Since the conversation went into [[ and ]]
>
> Before coming up with [_ and _] I very strongly considered [[ and ]] because I thought things should be orthogonal and different things should look different, not the same, and I thought that looked pretty different.

[_ _] is ambiguous though, because _ is also an identifier character.
[_x_] can be parsed as indexing with identifier _x_


>
> As I recall, I didn't like the look of it, and especially I didn't like that it seemed to be nesting something. However I did recall that python has """ (triple quotation mark to start a multiline string) which is like a special kind of quotation mark, yet consists of repeating the character, so I very strongly considered [[ and ]] or even [[[ and ]]] as the right solution. I just didn't think it looked that great.
>
> However to me the few examples given so far look pretty good with [_ and _] instead of [[ and ]]. Any more corner cases? keep them coming!
>
> By jpap:
> > [[T]] -- https://go2goplay.golang.org/p/NXqVM89QFor
> > [T] -- https://go2goplay.golang.org/p/L9AKNHxPkUU
>
> Here it is with [_ and _] (doesn't run, obviously):
> https://go2goplay.golang.org/p/i3eZOeR-oD6
>
> I find this extremely readable, like crazy readable if you remember that [_ _] is a mnemonic for fill in the blank.
>
> By Carla Pfaff:
>
> >MyMap[[string, MyList[[int]]]]
>
> Here it is with [_ and _]:
> MyMap[_string, MyList[_int_]_]
>
> that's really obvious to me if you consider that you're filling in the blank - MyList is a generic and you're "filling the blank" in with int, and MyMap is a generic and you're "filling the blank" in with string and MyList. I can't parse MyMap[[string, MyList[[int]]]] with my eyes even if I know what it's supposed to mean - I can't even see if the brackets are matched or unmatched, can you?
>
> Could people reply with more corner examples? I think a large wealth of cases would best show the different options.
>
> The team came up with a really clever way to let [ and ] itself be unambiguous (this whole thread, starting at the top), so I do think making it more explicit (turning those [ and ] into [_ and _] at the appropriate spots) could just be handled by gofmt. This would make the gofmt'ed output very readable forever and the user could still just enter [ and ] at the keyboard.
>
> Is there some collection of edge cases? Can people come up with some more?
>
> I think we should see how it looks especially at the corners and more cases and examples would show that best.
>
> --
> 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/CAFwChBn71m9%2B-DMTG5yH3g5TadKscm1X0WBwQKpzXyegxVN1PQ%40mail.gmail.com.

Robert Viragh

unread,
Jul 16, 2020, 5:59:45 PM7/16/20
to golan...@googlegroups.com
On Thu, Jul 16, 2020 at 11:50 PM burak serdar <bse...@computer.org> wrote:

[_ _] is ambiguous though, because _ is also an identifier character.
[_x_] can be parsed as indexing with identifier _x_

Yes, _int_ is an allowed identifier in Go.  However per the suggestion at the top of the present thread the compiler already knows where [ and ] signify a generic.  So if the user wants to use an identifier that begins or ends with a _ then in those spots the user must include the _ rather than have gofmt add it.  (Since gofmt would add a _ if there is no leading or trailing _ at those spots but wouldn't add one if there is one.)

In other words if you want to use the identifier _x then you would end up with [__x_], if you want to use the identifier x_ then you would end up with [_x__] and if you want to use the identifier _x_ (which is allowed in Go) you would write [__x__].

I think this is rare.  Keep them coming!  More corner cases? 

Matthias Mädel

unread,
Jul 16, 2020, 6:10:13 PM7/16/20
to golang-nuts
Just some considerations...

Generics syntax is a constraint of the Parser
Gen ops used with dB/ML/wire stuff...
Encapsulating parantheses follow logic
The type keyword shows the functionality
square brackets introduce a, not only visual,
distraction/confusion

I can accept draft as is

Robert Viragh

unread,
Jul 16, 2020, 6:23:30 PM7/16/20
to golan...@googlegroups.com
>I can accept draft as is  

I've changed my mind about my suggestion.  I can also accept and fully support draft as is!  It is fantastic.

--
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/7t-Q2vt60J8/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/34a6b157-ba22-4ea1-9067-8548a67b9a7ao%40googlegroups.com.

Dan Kortschak

unread,
Jul 16, 2020, 6:25:29 PM7/16/20
to golan...@googlegroups.com
On Thu, 2020-07-16 at 13:44 -0700, jpap wrote:
> Notwithstanding a concise unambiguous alternative, I would rather
> type parameters "stick out" so they are easily identified when
> visually scanning through code.

func ᕙ(⇀ X ↼‶)ᕗ GenericFunction(x X) ...


Denis Cheremisov

unread,
Jul 16, 2020, 7:49:17 PM7/16/20
to golang-nuts
func 🍺X🍺GenericFunction(x X) …

пятница, 17 июля 2020 г. в 01:25:29 UTC+3, kortschak:

Robert Engels

unread,
Jul 16, 2020, 7:59:53 PM7/16/20
to Denis Cheremisov, golang-nuts
Best proposal yet! I see increased Go adoption in the hipster /craft beer community. 

On Jul 16, 2020, at 6:49 PM, Denis Cheremisov <denis.ch...@gmail.com> wrote:

func 🍺X🍺GenericFunction(x X) …
--
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/c44e256d-23df-4b11-a487-e221c684d886n%40googlegroups.com.

Mandolyte

unread,
Jul 16, 2020, 9:45:25 PM7/16/20
to golang-nuts
Since readability is impacted by consequitive lists, you could factor it out...

with ( type list )
func ( arg list ) etc...

Dominique Devienne

unread,
Jul 17, 2020, 11:29:06 AM7/17/20
to golang-nuts
On Tuesday, July 14, 2020 at 11:56:01 PM UTC+2 gri wrote:

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


Nobody mentioned D's generics? https://dlang.org/spec/template.html  

The exclamation mark meaning "instantiate" reads well IMHO,
and the simpler single-type-parameter syntax omitting parens reads even better,
T!int is T instantiated (as in now, "at this instant"!) with int. FWIW, --DD

PS: Otherwise I agree that T[int] is nicer than T(int).

Jon Conradt

unread,
Jul 17, 2020, 11:56:32 AM7/17/20
to golang-nuts
In the spirit of “show me, don’t tell me” and experience reports. How hard would it be to release a beta which supports both [] and guillamets? We try them. We see where we have compatibility problems. We give editor writers and plugin writers a chance to simplify they keystrokes?

Jon

On Tuesday, July 14, 2020 at 11:37:21 PM UTC-4 Ian Lance Taylor wrote:
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
It is loading more messages.
0 new messages