
Go generics may use square brackets [] not parenthesis () - psxuaw
https://groups.google.com/forum/#!topic/golang-nuts/7t-Q2vt60J8
======
jiggawatts
Why don't they just use characters from the Canadian Aboriginal Syllabics
block?

[https://www.reddit.com/r/rust/comments/5penft/parallelizing_...](https://www.reddit.com/r/rust/comments/5penft/parallelizing_enjarify_in_go_and_rust/dcsgk7n/)

~~~
ed25519FUUU
> type ImmutableTreeListᐸElementTᐳ

> _If you look closely, those aren 't angle brackets, they're characters from
> the Canadian Aboriginal Syllabics block, which are allowed in Go
> identifiers. From Go's perspective, that's just one long identifier._

And the next comment is the same thing everyone else is thinking: “ _oh my
god_ ”

~~~
jussij
To be fair I suspect this part of that sentence explains the reason for choice
of these special character:

 _From Go 's perspective, that's just one long identifier._

The designer was using those characters to trick the Go compiler which I then
suspect means things like go format would also continue to work.

Then the designer could easily create a pre-processor that translates those
placeholders characters as a way of providing some sort of generics in Go.

So in reality this was quite a good solution as I can imagine coming up with a
solution that broke go format would have been a real pain to use.

~~~
mseepgood
> So in reality this was quite a good solution

Yes, I do not understand why people find this so hilarious. It's a clever
choice.

~~~
irrational
Because they look like normal angle brackets. Unless you are in on the secret,
you won’t understand why your code doesn’t work.

~~~
thaumasiotes
Easily fixed by using ᑅ ᑀ, ᕙ ᕗ, or ᗕ ᗒ.

~~~
jiggawatts
For some values of "easily" and "fixed".

------
chmod775
>A typical computer keyboard provides four easily accessible pairs of single-
character symmetrical "brackets"

"falsehoods programmers believe about keyboards"

On the keyboard layouts of many countries these keys aren't accessible at all,
either missing entirely or only being accessible via unergonomic key
combinations.

For this reason myself (and many other programmers) own additional ANSI-US
layout keyboards.

~~~
Akronymus
QWERTZ here. I need to use the following key combinations:

shift + 8 -> (

shift + 9 -> )

alt + ctrl + 8 -> [

alt + ctrl + 9 -> ]

alt + ctrl + 7 -> {

alt + ctrl + 0 -> }

Alternatively alt + ctrl can be replaced by altgr.

And for further fun:

alt + ctrl + ß -> \

shift + 0 -> =

shift + 7 -> /

Actually, it is probably easier to link the whole layout:
[https://de.wikipedia.org/wiki/DIN_2137#/media/Datei:Deutsche...](https://de.wikipedia.org/wiki/DIN_2137#/media/Datei:Deutsche_Tastaturbelegung_T1_nach_DIN_2137-01
--2018-12.png)

It is pretty bad for programming in languages that make heavy usage of
braces/brackets/parenthesis.

~~~
rk06
at that point, you might as well buy a QWERTY keyboard

~~~
hocuspocus
Everyone in our field should do themselves a favor and learn to touch-type
regardless of the physical keyboard.

~~~
Akronymus
I tried to learn the qwerty layout before, but my muscle memory is so
ingrained that it was impossible for me at the time.

And without correct keycaps it is even harder.

~~~
hocuspocus
Not being able to look at the keyboard is actually a good way to learn.

Of course you'll have to fight your muscle memory and type at 20 wpm for a
couple weeks. There's no shortcut.

~~~
Akronymus
My problem is that I need to be able to check where the keys are to be able to
learn it in the first place. I can touch type quite well, just not type at all
in a layout I never used before, without being able to look at the keys.

~~~
hocuspocus
Print out the layout and put it under your monitor while you learn.

~~~
Akronymus
That is such a good idea that I missed it for being so simple.

------
rspeele
I really like the syntax used to declare generic type parameters to functions
in ML-type languages like F#.

Writing the below in other languages (pseudocode) feels repetitive in the same
way that `Thing thing = new Thing()` did back before `var` or `auto` types
became popular:

    
    
        list<Y> Map<X, Y>(list<X> inputs, X->Y mapping)
    

Why do I need to put <X, Y> in brackets after Map? To tell the compiler that
these are generics, not attempts to reference actual specific types that just
happen to have crappy, nondescript names like X and Y.

In F# and friends, there is no need to put a bracketed block declaring which
types are generic parameters. I would simply write:

    
    
        let map (inputs: list<'x>) (mapping: 'x -> 'y) : list<'y> = ...
    

The little apostrophe or tick mark before the type name tells the compiler
it's a generic type parameter. It's not legal to name a concrete type with a
leading tick mark, so there can be no mix-up. At first it looks ugly, but once
you are used to it, it is natural and less cluttered than having to have these
separate bracketed declarations for "uh-oh, here comes a generic".

But I suspect Go will always want more explicit syntax for generics. In part
out of backwards compatibility, but largely because Go's designers see
generics as an advanced, occasionally useful feature. Correspondingly they'll
want the presence of a generic function to have ugly syntactical warning
signs: here be dragons.

Functional languages usually take the opposite approach and see a fully
generic function as _less_ complex than one for a specific type, because just
by looking at its signature, you know it doesn't require anything special
about the input objects it's going to work on. It is not going to be calling
methods on them or messing with their guts. It's just treating them as black-
box, could-be-anything cards to be shuffled around from point A to point B.

~~~
a1369209993
> Why do I need to put <X, Y> in brackets after Map? [...] In F# and friends,
> there is no need

Try:

    
    
      let foo (arg: list<'x>) : list<'x> =
        ...
        <inside foo somewhere>
        let bar (arg: list<'x>) : list<'x> = ...
        ...
    

Is bar generic over a fresh type that happens to be named 'x, or is it a
monomorphicly-typed local function that uses the type 'x that foo is generic
over?

~~~
uryga
not sure about F#, but in haskell it's the former, and you can switch to the
latter with the -XScopedTypeVariables language extension. though IME you
rarely (if ever) need it thanks to type inference.

~~~
a1369209993
Actually, on further consideration, you could restrict the ' to only occur
where the type parameter is _introduced_ , like so:

    
    
      List<X> foo(List<'X> inp) =
        ...
        List<X> bar(List<'X> xs) = ... # new X scoped to this declaration
        List<X> baz(List<X> xs) = ... # use foo's X
        ...

------
ca_parody
To me, Go feels condescending to write without generics. I may just not groke
the idioms - but putting users in a walled garden that the standard library
has the privileged to escape from (i.e. (Map<k, v>)) just seems to go too far
in not trusting the programmer. The fact that generics have taken so many
years - to still be talking about what [<( to use is beyond me.

There is a difference between becoming C++ and allowing programmers to make
fundamental abstractions without interface{} hacks.

~~~
throw_m239339
The problem is not so much the absence of generics but the availability of
crazy reflection at runtime compared to the rigid type system at compile time.
There absolutely is a lack of balance here.

All these things were already pointed out 10 years ago and were met by "you
don't need that with Go", "Go back writing Java" sort of contempt, which gave
the Go community a bad reputation.

Go could have been fixed 10 years ago if Rob Pike and co listened. They didn't
want to because they thought they knew better than everybody else. ADA already
fixed the genericity problem while keeping things readable with a limited form
of generics as incomplete types.

~~~
pjmlp
As I already mentioned a coupled of times, even CLU like generics would have
been a good enough solution.

Something that they acknowledged not bothered to look at initially.

[https://go.googlesource.com/proposal/+/master/design/go2draf...](https://go.googlesource.com/proposal/+/master/design/go2draft-
generics-overview.md)

> We would have been well-served to spend more time with CLU and C++ concepts
> earlier.

------
hota_mazi
> A typical computer keyboard

A typical NORTH AMERICAN computer keyboard.

Does the myopia of the Go compiler team know any bounds?

~~~
IshKebab
You mean a typical English keyboard?

Ironic that you are criticising them for only thinking of the US, but you've
done exactly the same thing.

~~~
lixquid
The ISO UK keyboard layout exists. The North American keyboard layout is North
American, not "English".

~~~
p0llard
This is true, but an ISO UK keyboard _also_ "provides four easily accessible
pairs of single-character symmetrical 'brackets'"; for this particular purpose
there is no distinguishing factor between the two layouts.

------
marcrosoft
I wish they would just freeze the language and not add anything. I like it the
way it is. I feel like the general attitude of software developers is: if it
isn’t changing and being updated it’s dying. I think we should start putting
weight on stable unchanging software.

~~~
deanCommie
Meanwhile I will never use Go until it has generics.

I wonder if there is more of people like me or people like you...

~~~
axaxs
Based on the popularity of the language alone, I can say rather definitely the
latter.

~~~
MereInterest
I'm sure that being backed by the largest software/advertising company in the
world has nothing to do with its adoption rate, and that popularity is a
direct mapping of quality of a programming language.

~~~
shirro
The Google backing and hype kept me away from Go for years. After being stuck
with a succession of scripting languages that weren't doing it for me and
being frustrated at the time it takes to be as productive in something like c,
Go is really the sweet spot of readability, reliability and productivity for
me. I think the Google backing may not be as advantageous as some people
think. Dart did not become a popular language and would probably have died out
if Flutter hadn't come along.

~~~
pansa2
Yeah, Go being from Google is not necessarily a good thing. It means the
language is under the control of a single company and, what's more, a company
that is well-known for giving up on many of its projects.

~~~
dhagz
And the open-source aspect of Go's development is managed entirely in Google
services (the dev mailing list is a Google Group, you can't contribute without
a Gmail address...the list is long). Sure it's hosted on GitHub, but the
majority of the real conversations are elsewhere.

------
shirro
I really like the Go standard libraries. I can do most of what I need with
them. I worry some of the people calling for generics will move the language
in the direction of massive abstract libraries. I don't want to include a Go
version of Boost in everything I build. Complicated type signatures seem like
an unnecessary additional burden for average programmers.

I really hope generics, like reflection, are only used when there isn't a
simpler option. I honestly would have preferred and had more use for sum
types.

I like the square brackets though. They look right for Go and it is more
readable.

~~~
_bxg1
I haven't used Go but I'm curious, how do core data structures like hash maps
work without generics? Does everyone just roll their own for their particular
use-case?

~~~
echlebek
The built-in types map, chan, array, and slice take type parameters, and they
are usually sufficient for doing whatever you need to do.

It's incredibly great that, generally speaking, if I'm looking at Go code, the
only kind of hashtable there can possibly be is the Go map data structure. The
only kind of blocking queue there can possibly be is the Go chan data
structure.

You will never see a LinkedTreeDeque or whatever other bizarre concoction
someone might come up with. And people don't feel obligated to make every API
an iterator of some kind.

Go makes being generics architecture astronautics impossible, and I really
love that about it. Perhaps that makes me basic, but I am basic and happy.

~~~
throw_m239339
> The built-in types map, chan, array, and slice take type parameters, and
> they are usually sufficient for doing whatever you need to do.

They are so sufficient you need to learn all these tricks by heart in order to
do basic operations on slices:

[https://github.com/golang/go/wiki/SliceTricks](https://github.com/golang/go/wiki/SliceTricks)

------
verandaguy
Honest question; why use a comparatively exotic syntax for generics? Most
other similar languages — Java, C# — and perhaps most notably C++ since its
the closest analogue in terms of paradigm and domain, all use angle brackets
(without getting into the semantic differences between "classic" generics and
the STL).

AFAIK, Golang assigns no special syntactic meaning to angle brackets beyond
comparison operators (correct me if I'm wrong though, I don't write much Go),
and using a familiar syntax would make it easier to work with coming from
other languages.

~~~
remus
From the post:

> Angle brackets require unbounded parser look-ahead or type information in
> certain situations

So they want to avoid angle brackets so they can efficiently parse source
code.

~~~
verandaguy
Thanks! I'll have to look into this some more but if anyone can volunteer the
information, these questions come to mind:

\- What situations could cause the look-ahead to be unbounded?

\- How does the Go parser's implementation differ from those of the languages
I'd previously listed which all also have angle brackets for comparison (
_and_ bit shifts, as another commenter pointed out) while apparently avoiding
unbounded look-ahead?

\- What challenges does the Go parser present when trying to adapt it to
follow patterns more like those from the other languages' parsers?

\- I'm assuming "unbounded" here doesn't mean "it could take unlimited time"
but rather "it could incur non-negligible compile-time penalties;" do the
benefits of a slightly-quicker compilation (which has nominally no impact on
runtime performance) outweigh improved readability?

~~~
ogogmad
Unlimited lookahead parsers are not necessarily inefficient in terms of time.
PEGs for instance run in linear time, but use up linear memory, which is the
problem:
[https://en.wikipedia.org/wiki/Parsing_expression_grammar](https://en.wikipedia.org/wiki/Parsing_expression_grammar)

> \- How does the Go parser's implementation differ from those of the
> languages I'd previously listed which all also have angle brackets for
> comparison (and bit shifts, as another commenter pointed out) while
> apparently avoiding unbounded look-ahead?

I think parsing Java or C++ requires unlimited lookahead.

~~~
echlebek
[https://blog.reverberate.org/2013/08/parsing-c-is-
literally-...](https://blog.reverberate.org/2013/08/parsing-c-is-literally-
undecidable.html)

------
ivoras
> Angle brackets require unbounded parser look-ahead or type information in
> certain situations

So.... they'd rather pull the "it's hard to do" or the NIH card, than make it
easier for developers switching between languages? Interesting choice. Someone
mentioned "condescending" \-- that description fits perfectly.

And there I was thinking that COMPILER WRITING should be hard - as hard as
possible - in order to make the code which is to be compiled as easy to write
as possible.

~~~
breakingcups
I think they actually make a fair point because in this case "hard to do"
probably equates to "makes the compiler a lot slower".

Go is very easy to switch to from most other languages so I don't think making
it harder to switch is a design goal from the team.

When Go came on the scene, I actually really appreciated the fact that they
truly explored what a new language could do differently, especially because it
was coupled with a strong philosophy.

Now, since 1.0 there have been plenty of decisions made which I dislike.
Still, I think the glasses through which you choose look at the Go teams
motive aren't fair. Why _wouldn 't_ designers of a programming language like
Go think it through very carefully, weigh every angle and decide to settle on
a lesser used but possible equally valid solution compared to 80% of the
languages out there?

~~~
centril
> I think they actually make a fair point because in this case "hard to do"
> probably equates to "makes the compiler a lot slower".

They are wrong in this case.

When considering whether to allow `foo<T>();` in Rust, we measured the
performance impact that infinite look-ahead / backtracking would have in that
case. The result was negligible.

Why? Because the amount you have to look-ahead, before you can say "definitely
wrong; let's turn back", in realistic code is bounded to maybe max 30 for some
very complex generics. It's however much more likely that no back-tracking
occurs at all because the parse will be correct.

When engineering your compiler, you can always make choices about which path
is statistically more likely, as back-tracking in a well designed language is
usually pathological. The theoretical O(..) complexity is largely irrelevant.

(Source: I was the main maintainer of rustc's parser and refactored large
parts of it.)

------
thenanyu
If the type keyword needs to be added anyways, why not angle brackets then? Is
it still ambiguous?

~~~
pavon
The way I read it, the type keyword would only be used in when declaring
generic functions/types, but the angle brackets syntax has clashes when
calling generic functions.

------
wbl
Just use guillamets already.

~~~
djhaskin987
Link for those who are curious:
[https://en.wikipedia.org/wiki/Guillemet](https://en.wikipedia.org/wiki/Guillemet)

~~~
rohan1024
‹‹››

------
emmelaich
I know it's unpopular but judicious use of significant white space would solve
the ambiguity too.

------
rurban
Perl allows optional delims if ambigious. so if the best set <> is ambigious
double or triple them. But much easier would be to demand uppercase T type
variables.

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

w is lowercase, unambigious. Parsed as list of comparisons.

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

Ambigious, so use

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

instead. These cases are very rare. Or demand parens for such ambigious lists
of comparisons. Might be too late already.

Of just demand the type keyword there also:

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

------
pjmlp
If they finally end up adding any kind of generics support, it feels much more
natural, from my experience with languages that use [] and () for type
parameters, [] are much better visually.

------
tapirl
This is a good move, which will make Go more consistent. Making custom generic
and builtin generic consistent absolutely makes Go more easy to learn and
read.

------
kitd
I liked the alternative proposed later in the thread:

    
    
        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
    

or using `forall`

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

V readable IMO

------
deanCommie
> Angle brackets require unbounded parser look-ahead or type information in
> certain situations

I wonder how this is solved in Java, and C++, and C#, and...

~~~
rsc
It is solved by doing unbounded parser look-ahead (if you need that) or by
requiring that all tools have full type information to understand programs (if
you need that).

It is definitely better for tooling not to need either, both for human readers
and mechanical tools

The example in the mail shows that using angle brackets with Go would require
full type information, making simple tools like gofmt impossible.

~~~
deanCommie
> making simple tools like gofmt impossible.

So now we're making language design decisions based on the limitations of the
bonus linter?

My opinion of Golang drops more and more with every new detail I learn about
it...

~~~
doctor_eval
One of the benefits of Go is super fast compilation time. Unlimited look ahead
is likely to make the compiler slower. Ergo, it’s not a great idea.

~~~
estebank
Parser speed is negligible contributor to slowness in any compiler. rustc has
a couple of places were it performs bounded lookahead to supply suggestions
for incorrect code, but the grammar of the language is regular. 3rd party
tools don't have to worry about it, and users get accurate suggestions. The
cost is 1) you have to implement a more complex parser while 2) making people
go "you already know what I meant, just take it without me using the 'ugly'
turbofish". It think this is the correct approach for language design.

------
alvarelle
I did not see digraphs mentioned. Could look like <: and :>.

The formatting tool could even normalize that to some prettier unicode.

------
hedora
Are they adding Java style generics (type erasure), or C++ style (compile time
specialization)?

~~~
mappu
The go2 proposal (as currently implemented) is a source-to-source translator
back to go1, so neither in that case.

As for the existing implementation of generic map[K]V, generic []T, chan(T),
etc., it's a best-of-both-worlds mix - a kind of erasure (there is only one
map implementation in a Go binary) but with a static vtable for accessors
without Java's need for boxing - all possible owing to the clever coupling
between the runtime and the compiler.

See [https://dave.cheney.net/2018/05/29/how-the-go-runtime-
implem...](https://dave.cheney.net/2018/05/29/how-the-go-runtime-implements-
maps-efficiently-without-generics)

------
_ph_
Honestly, I prefer the parenthesis notation to the square or angled brackets.
It is perhaps a bit verbose, but easy to read in my eyes. Not to mention the
keyboard problems with non-English keyboard layouts.

------
chvid
Doesn't [ ] have similar problems? Suppose I write:

a[x](z)

Is that a read of array a with index x followed by a function invocation with
the parameter z.

Where it could also be a type conversion of z into generic type a of x.

~~~
earthboundkid
The issue isn't the interpretation; it's the parsing. It doesn't matter to the
parser in this case if x is a type or an int. You'll have the same AST either
way. But if you have f<a,b>(x), it matters if this is a generic or a pair of
comparisons because the AST would be totally different.

------
coldtea
Funny how we add 100 new emoji per year, and yet we can't seem to be able to
add a new matching pair of characters in our keyboards for 5+ decades...

~~~
josephcsible
How many of those emoji are on your keyboard?

~~~
coldtea
On my iOS virtual keyboard that would be all of them. Heck, the Mac laptop's
touch strip also features a selection.

But that wasn't my point. It was that we get a ton of BS cosmetic/fun items on
the Unicode standards, and we can't get a few simple glyph additions on our
hardware keyboards that would seriously help programmers for decades to
come...

------
jfoutz
seems like you could just not use ',' as the separator character in type
declarations. +-*^% I think have the same problems, but @#$ are available.
weird syntax, but go already makes the tradeoff, do what's fast to parse.

------
nemothekid
You have emoji on your keyboard?

------
IshKebab
This is much more readable IMO.

------
arun6582
Whatever!! Go finally brought generics. The same thing which it used to
criticize java for Lol

------
tenderfault
can someone just stick this thread forever on top of HN listing

------
Chloro
If go gets generics I will never use it again.

~~~
jagger27
Why?

~~~
72deluxe
I wondered this too. It's not like there's a part of a language you MUST use.
Perhaps they are fearful that it'll grow and grow and add features in that
will make it difficult to learn and unwieldy. Stroustrup always said that new
languages seem fast and easy to learn precisely because they are new languages
and don't have the features of maturity. Perhaps this is the case here.

~~~
pjmlp
He is quite right, yes C++ is a monster dragon, but most languages of similar
age only look simple on the surface, even a beginners friendly language like
Python.

Beneath that simple surface for beginners lies a multi-paradigm language, with
overloading, metaclasses, dynamic code generation, multiple inheritance,
breaking changes across minor versions in 30 years of history, a standard
library that can be used as doorstep when printed, several implementations not
100% semantic compatible with CPython,.....

~~~
72deluxe
Yes, it's good isn't it!

