Hacker News new | ask | show | jobs
by lsllc 2192 days ago
Interesting article. I think that using <> for the type specifiers would possibly be better! For example one could quickly end up with something like:

  func (obj *SomeType(Q, Z)) Foo(type K, V comparable)(key K, val V) (*OtherType(Q, V), error) {
      ...
  }
... Lots of Infuriating & Silly Parentheses?
6 comments

On <> syntax, this has been discussed quite a bit. () were chosen to avoid ambiguous parsing.

Also note that generic methods are not allowed in the current design, only generic functions.

The new draft design ( https://go.googlesource.com/proposal/+/refs/heads/master/des... ) discusses both of these points. I recommend everyone who is interested in this topic read the design spec, ideally before commenting.

On the <> syntax, why not improve the parser? Every language that uses <> for generics has a parser that is able to distinguish between generics and the "less than" operator, and it seems odd that Go developers think it can't be done efficiently.
C++ and Rust require extra disambiguating syntax in some contexts (C++ the `foo.template bar<T>()` syntax, Rust the "superfish operator" `foo.bar::<T>()`. Java works around the problem by awkwardly putting the generic argument list in front of the method name (`foo.<T>bar()`). I don't know about C#.
C# is `foo.bar<T>()`, I’m not sure what compromises that had to make for that to work but from an end-user perspective it works well.
They parse it one way, and if they figure out the other parse option was right, they go back and fix the parse tree.

Basically, unlimited look-ahead.

Is it somehow a problem?
(teeny tiny note: turbofish, not superfish)
Oops :D
Using [] is the optimal choice for languages in general, but they can't even use that because they blew away that syntax for indexing.
Scala uses [] for generics and () for indexing, which sort of makes sense given that in the end arrays are just functions.
So are the uninstantiated generic types ;-)
Go developers like to claim that the reason the Go compiler is fast is because it's "simple to parse". Unfortunately this doesn't make a lot of sense as parsing is typically only 1-5% of the total compile time.
It's not only about the main compiler. Go has tons of third-party tools (linters mainly) that can parse go code, because it's so easy to write one. Most of them wouldn't exist if parsing was a PITA.
Those almost universally use the "ast" package provided by the stdlib.
It is not a question of "Improve" but a question of dealing with tradeoffs. The Go parser is built so that at every stage it is totally unambiguous what the parser has to do.

This reduces the amount of state that the parser has to carry around and makes the error messages for syntax errors easier to generate.

Languages that use <>'s for generics have to look at a larger amount of the code when parsing to work out what to do.

In my opinion, the new design doc clearly characterizes this as a design goal of the parser, rather than a hard constraint that cannot be resolved.

"Resolving that requires effectively unbounded lookahead. In general we strive to keep the Go parser simple."

It's totally fair to question the tradeoff - should having a simple parser outweigh the potential ergonomic benefit of <>? I don't know. The forum for this is probably the golang-nuts group.

The design doc doesn't say "simple", it says "efficient".

I think "simpler" would have been a stronger argument. While using <> would make the parser more complex, I have a hard time seeing that making a meaningful performance difference in a compilation context. Maybe if you're parsing a lot of Go without actually compiling it, but that doesn't seem like a use case to optimize for.

It's all a matter of where you accept the complexity - in the compiler, in the language, or in the downstream applications. In my view it's an obvious choice to choose a more complicated compiler on exchange for simpler downstream applications...
The main reason is probably that by omitting <> for generics makes the symbol table unnecessary as part of the compilation stage. Go and D are the two modern languages that are avoiding symbol table like a plague for faster compilation time, i.e. the code should be parseabled without having to look things up in a symbol table .
The golang approach has been to dumb down the parser, at the expense of making it more complex for users.
there is no complexity tradeoff for users here.
Angle brackets are easier to parse for humans.
Ah, right, I missed this:

https://go.googlesource.com/proposal/+/refs/heads/master/des...

Hmmm ... not sure how I feel about that. Ok so my original example becomes this:

  func (obj *SomeType) Foo(type K, V, Q comparable)(key K, val V) (*OtherType(Q, V), error) {
      ...
  }
Which is only slightly better [without the generic type].

Don't get me wrong, I'm a big fan of Go, but I'm kind of on the fence about generic types. I've made do with casting interfaces and type-casts for many years and I'm OK with it (honestly, glad to not be a C++ or Java programmer anymore).

> Ok so my original example becomes this

No, you still don't understand. Methods can't have additional type parameters, only functions. This part is not allowed in your "example":

    (type K, V, Q comparable)
The doc says:

> Generic types can have methods. The receiver type of a method must declare the same number of type parameters as are declared in the receiver type's definition. They are declared without the type keyword or any constraint.

So my original example should have been:

  func (obj *SomeType(K, V)) Foo(key K, val V) (*OtherType(K, V), error) {
        ...
  }
(hopefully this is now correct!).
Exactly. Thanks!
I never have been able toi read the syntax with < > well, so I am soo happy they found some syntax based on parens. It is perfectly readable for me.
After seeing and trying dlang syntax for generics I was hugely disappointed that rust chose angle brackets.
I'm ready to bet that with <>, you'd introduce ambiguity in the grammar, making parsing code (and, as a consequence, writing tools for the language) much more complicated.
I agree, I think from a visual perspective the type declarations blend in with the rest of the type/function signature. I'm curious how quickly I'll get used to it.
There is absolutely no way this gets the vocal "conservative" core of the Go community's approval. For better or worse.
The <> people are the conservatives, because they are afraid to give new things a try. They want every programming language to look the same.
Consistency is important, though. Using C-family syntax makes it easy to get people to dip their toes into something within the spending time to learn a new syntax. Look at modern languages like Rust, Swift, Dart, and Go: they all use C-style syntax.

I’m not accusing you of advocating Go’s adopting a different syntax for generics solely to be contrarian - but using angle-brackets for type-parameters and template-parameters is a proven technique with few downsides - and certainly not any that downsides that would be fixed by using any other syntax I’m aware-of.

Well, Go already picks and chooses elements from different languages, e.g. Pascal-style declarations ("x int" instead of "int x") and C-style blocks ("{...}" instead of "begin...end"). So I'm not surprised that when deciding on the syntax for generics they look more at what fits best for Go than at what other languages are doing...
I do prefer TypeScript’s version of Pascal-style declarations now. Though I really don’t like how Go doesn’t have a colon between the name and the type, it makes it harder to scan-through parameter lists when you aren’t using an editor with syntax colouring.

(I had to write a lot of Go code in Notepad for a while in 2017 - never again)

Now that unicode is everywhere I think it's time to use more brackets than [], (), {} and <>.

Python is the worst, where dicts and sets both use {} and tuples and "grouping" both use (), each causing real problems.

Some of many possibilities:

⦅⦆«» ⟦⟧ ⟨⟩ ⟪⟫ ⟮⟯ ⟬⟭ ⌈⌉ ⌊⌋ ⦇⦈ ⦉⦊ ||

Yes, you need a way to type it on non specialized keyboards.

An easy way is is to type it as (( or [[ etc, and let the IDE convert it.

To me this is the ultimate "developer chasing shiny" at the expense of everything else.

Especially in the context of Go that hasn't even managed to use anything other than parentheses in its func definition syntax. No language has saturated the bracket options that come with the standard US keyboard so much that it's worth bringing in characters that you need additional tooling to type.

If (), <>, {}, and [] aren't enough for a language, something has gone very wrong.

While you normally don't regard it as a bracketing we also use "", '' and `` to bracket things.

In Perl / / was used to bracket regrexs in C and many C languages /* */ is used to bracket comments.

Dicts and sets are supposed to both use {}; {a,b,c} is (conceptually) shorthand for {a:dummy,b:dummy,c:dummy}, where sets (in some cases, including python, opaquely) are actually dicts without the space to store values. (This also shows up in set theory and related mathematics, although there it's dicts that are unusual.)

The rest of your post is a fair point, although I think you underestimate the value of plain ASCII that can be typed on a unassisted keyboard.

That's a lot of effort for little benefits

I can already see a lot of edge cases

Most notably font support

I like ligatures and ide/editor support for them, but I'm not sure I like this

The benefit is purely readability.

You read code 1000x more often than you write it, so a little extra effort for even a small readability benefit is worth it.

Most fonts have quite full Unicode support since many years.

I'm often disappointed in how reluctant programmers are to use modern technology. The public thinks we're on the cutting edge of advanced technology. If they only knew :)

This is just unnecessarily salty. Fonts may have full unicode support, but do keyboards? I don't want to have to press some weird key combination to type a symbol I might type hundreds of times each day.

Honestly, it's just common sense.

> The benefit is purely readability.

This year mark my 30th year as a professional developer, but my first "program" was copied fro a magazine

   10 PRINT CHR$(205.5+RND(1)); : GOTO 10
It generated random maze like structures in screen using two PETSCII symbols

I think everybody know this one liner here

So it's not symbols I don't like, it's the lack of ergonomics

My hands started hurting when I had to type a lot to write simple symbols like } that's why I switched to US layout and things have gone a lot better for my nuckles

What really surprises and disappoints me is how many people appoint themselves as engineers or computer scientists, but take for granted that their ideas are good and the rebuttals are "resistance to change" without even reading the most basic studies on the topic or testing their theories on the field.

Implement your idea, measure the results and then we'll discuss of why they didn't take off.

Because they won't, believe me.

There is a reason why I use font with the zero striked, otherwise capital O would look too similar.

What do you think of ‹‹ vs « ?

Do they really look different enough to be useful?

Cutting edge doesn't mean "stupid"

Even Spanish keyboards only have a dedicated Ñ key, so you have to use multi-key sequences to input ÁÉÍÓÚÜ etc.

And even after typing in Spanish for five years, it's still objectively more annoying to write está (6 keypresses) vs esta (4 keypresses). And I'm always having to go back and correct the key sequence because I've written ´a instead of á.

Also, just today I saw someone use "⇸" (crossed out arrow) on HN and it was so tiny with HN's font that I had to zoom in to see what it was which only inhibited their message.

You have to consider this sort of overhead when making decisions about the glyphs you are going to impose upon everyone when designing a language.

I've seen pages and pages of bike-shedding over whether to use kebab-case over snake_case for a DSL because it's one less keypress to type a hyphen vs underscore.

I can appreciate your preference for those characters. It's nice how they can encode meaning in a single glyph where you would otherwise need multiple glyphs (like "!="). One solution that you can find people doing today are IDE plugins that simply rerender something like "->" as "→".

Seems like the best of all worlds. You get to work with higher level characters while the plaintext format remains in the most accessible common denominator.

I do like what Haskell and Scala have done for operator diversity ... maybe Go should learn from it:

https://www.reddit.com/r/programming/comments/a9tb2/secret_h...

I'd rather have

    [* *]

    [= =]

    [/ /]

    (* *)

    (= =)

    (/ /)
Just kidding, requiring specialized tooling to type isn't quite acceptable..
Half of those don't even render for me. French quotes seem fair though, since we throw $'s around in languages like every country needs a dollar sign on their keyboards.
And then you need a specialised IDE to type characters?

Or there's a bug in the IDE and all of a sudden you literally cannot type anymore?

It's a chicken and egg problem. Input systems could be a lot better, and we wouldn't need (as much) a specialized IDE. E.g., in Linux, I can type « and » if I map the compose key (which I do, but it isn't the default). For really wild characters, OS X has a pretty decent character search dialog (^+⌘+Space) though it has a few usability shortcoming that I really wish Apple would address. (I wish I could get a decent implementation of this in Linux, and I wish I could get a compose key in OS X.)

(I'm not sure I'm convinced that using such symbols in a programming language is a good idea, of course, just that our current input tech is barely trying.)

Also, APL happened.

How do I pronounce... any of those?