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.
w(<x,y> z)
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
generic func F[Mult: Int](X:Int) y:Int: begin y := Mult * X end
--
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/97a2914f-3e54-4994-974f-135e11f11117n%40googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/3E782FE9-D854-45D8-B454-EC869409EA5D%40iitbombay.org.
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.
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."
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.
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..
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)
func f(T|int|)
struct{ T|int| }
interface{ T|int| }
[]T|int|{}
type S|T| struct { v T }
func f(T[[int]])
struct{ T[[int]] }
interface{ T[[int]] }
[]T[[int]]{}
type S[[T]] struct { v T }
// 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),
}
}
// 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),
}
}
--
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/1e8397a5-bcc9-48b8-924b-1d35f4434d14o%40googlegroups.com.
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_]{}
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))
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).
> 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.
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.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/9ac6d86f-7647-4814-b15f-67f5cec5dadcn%40googlegroups.com.
--
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/abe84ff5abefe2815137bea9cb83eb9e8d5fdafb.camel%40kortschak.io.
var node Node<int>
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 intdata 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.
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAOyqgcX-OXktNtUs0G4Ns0iEr3R2qLPpU7q1%3DrOY93%3DAO16a3g%40mail.gmail.com.
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.
--
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/abe84ff5abefe2815137bea9cb83eb9e8d5fdafb.camel%40kortschak.io.
--
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/CAA40n-WEv4uoa0eSyE5M2yimn6tPJ7_vHFxPW5W2mCk1zTZfmg%40mail.gmail.com.
"42".parse::<i32>()
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 Treturn 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.
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.
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 don't think [[T]] is offensive to the surrounding code, and would love to know what more people think.
[_ _] is ambiguous though, because _ is also an identifier character.
[_x_] can be parsed as indexing with identifier _x_
--
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.
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.
with ( type list )
func ( arg list ) etc...
Not unexpectedly, many people raised concerns about the syntax, specifically the choice of parentheses for type parameter declarations and generic type and function instantiations.
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