back
66 comments
tl, dr; This post ended up way longer than I expected. Super science summary: I wish Go had tuples, unions, constructors, no Nil, exceptions, generics, some syntax sugar, and ponies that shoot Cheez Whiz out of their noses.

There's always D, if you want more whiz bangs. It's been oft quoted, that good library design involves leaving someone wanting more. I think one form of good language design involves this too. Go is supposed to be minimalist. I think it's a good sign if people keep wishing it were just a little less minimalist.

If you read the post instead of just the funny tldr, you realize that many of the demands would make Go simpler and/or more orthogonal and/or smaller.
Can you flesh that claim out a bit more? I read the post and it was almost all extra stuff that would make the language spec longer, not shorter.
If you have actually used Go, you would realize that many of his demands display seriously lack of familiarity with the language.

For example, he completely missed defer.

I'll have to agree with the D comment. As someone who actually writes in D (for fun), I felt that it was more like a checklist for D than a wish list for Go. Though yes, I will concede that Go + those changes != D.

Unfortunately, the Cheez Whiz ponies aren't implemented yet, but if D ever hits 3.0 maybe it can be pushed into the spec.

I got the impression he wanted something close to Ocaml than to D. D is much more complicate than what he is proposing.
I do think D is probably closer to where my heart is. I think my ideal low-level language is a bit like C + some of ML's type system. I think it may be possible to get that without being as heavyweight as D, but I'm not sure.
> Unfortunately, the Cheez Whiz ponies aren't implemented yet, but if D ever hits 3.0 maybe it can be pushed into the spec.

I'd wait first for the any of the existing D2 compiler(s) to at least implement the features that are in D2 'spec', as it currently stands there are no implementations of D2, and that is without going into all the bugs in the most used compilers.

Go actually has a fair amount of syntactical sugar, the language design document has a fair amount of "ooh! And we did this cool thing to save you from typing an extra line!"
I especially like "short variable declarations" (aka type inference, http://golang.org/doc/go_spec.html#Short_variable_declaratio...). Just type one more character than you would in Python for assignments, but you get static typing.
Go is supposed to be minimalist.

Every language is designed according to that rule. No language designer thinks, "Let me just slap some extra unneeded stuff in there." They just differ on what they consider necessary.

In Go's case, I think many of the things they've omitted from the language are actually useful features and they've just pushed the complexity onto the users. For example, no exceptions just ends up meaning "if err != nil { return nil; }" everywhere.

Every language is designed according to that rule.

False. Ruby and Perl were both designed (at least for a time) by adding stuff people were asking for or talking about. Not all language designers set a high priority on the thought, "Can we do without it?"

Any complexity not covered over by the language will end up in the user's program.
Any complexity not covered over by the language will end up only in the programs that need it, as opposed to being included in every program. If the complexity is common enough, it's probably worth centralizing (like, say, an object system or type checking), but designing a language so that tricky things that 95% of code doesn't use have special support results in a language that will wow folks on the internet but piss off those using it for real large projects.
I mostly program in Clojure now and I have only used Go a little bit. However, I really like Go.

I have a lot of respect for the people who developed Go. I'm sure in their many years of designing operating systems and languages that the features described in these obvious criticisms were considered. That is not to say the language shouldn't be criticised but, in this case, a little research would have made the article a more interesting read.

To take two examples. Constructors are easy to write in GO:

http://sites.google.com/site/gopatterns/object-oriented/cons...

An exception-like mechanism has been proposed:

http://groups.google.com/group/golang-nuts/browse_thread/thr...

Constructors are easy to write in GO:

Maybe I didn't clarify it enough, but I tried to explain my problem with initialization functions in the "Future-proofing" section: "If you later need more complex initialization, you’ll have to replace every new(Foo) call with NewFoo()."

An exception-like mechanism has been proposed:

Yes, and implemented and put in the language and referenced in the post. I'm not crazy about panic and defer for reasons spelled out a bit here: http://www.reddit.com/r/programming/comments/du9zu/the_langu...

> An exception-like mechanism has been proposed:

Proposed and implemented almost six months ago.

But that doesn't stop people from writing criticisms of the language and asking for their pet features after a cursory watching of Rob's original (and very superficial) talk. sigh

Panic isn't really exception-like - it's explicitly described as not for transmitting exceptions across module boundaries.

There's more discussion, by me and others, in this past entry: http://news.ycombinator.com/item?id=1576401

A better error handling mechanism may have been a restartable condition system.

Exception-like is not the same as proper exceptions. From what I've read about it (there's the caveat: I haven't used it yet), the error handling model in Go sounds pretty badly broken.
"Now when you need to read from a file, you just do:

    ReadFile(filename, func(file) {
        fmt.println(file.Read())
    });
Now you’re safely guaranteed to close the file when the operation is done. This works because Go has lexical closures, a really nice feature. But the syntax for this is ungainly. Ruby addresses this with block arguments. Translated to Go, they could look something like:

    ReadFile(filename) do(file) {
        fmt.println(file.Read())
    }
"

He does realize, that all he did was replace "func" with "do", and moved some parentheses around, right?

Are there other examples where the difference is more dramatic than this?

Of course he does. And yes, this small syntactic difference does have effects in the real world; rename 'func' to 'delegate', and with the addition of a semicolon (at the end of the println() call) this is valid C# code today - but almost noone writes their resource protection using this idiom because it's ugly.
> this is valid C# code today - but almost noone writes their resource protection using this idiom because it's ugly.

Incorrect (and you don't need delegate, C# has lambdas now). It's because C# doesn't use that for its stdlib and instead has a (redundant) special form and defined protocols.

Totally agree about Unions, and I think that one of the reasons that nil is there might be because there aren't any yet.

I'm not sure I agree about overloading, though. Maybe something like Haskell's infix notation (1 `add` 2) would be good enough.

The discussion over at golang-nuts, the official mailing list - http://groups.google.com/group/golang-nuts/browse_thread/thr...
"Once you have constructors and you can statically ensure that every variable has a chance at initialization, you can start to escape one of the most unfortunate language misfeatures in wide use today: null"

Question, can anyone think of a case where the lack of pointers being able to point to null will make it more difficult to implement something? That is will you lose expressivity if this becomes impossible?

The only case is when you want to express data is missing (not that a variable is unitialized, but that there's nothing there, think ternary boolean) and that is very neatly taken care of by option types (ML's `option`, Haskell's `Maybe`) which you can easily replicate via union types (create a type Nothing with a single value `nothing` and replace what was formerly a nullable type `T` by the union type `Nothing | T`; depending on whether you want these to be easy to use or not, you may or may not want to add the ability to lift computations into the option type)
Right. The language makes you check for nulls at the few places where the result can go null, and all other tedious null-checking goes away forever.

All the advanced type system jiggery-pokery aside -- that's probably the best "normal programmer" example of why ML & Haskell do static typing better. The ALGOL family of languages should have done that years (decades!) ago.

So many I wouldn't know where to start. Databases have had not-null constraints for years, but an accurate model often means having null values.

A better improvement would be to handle null references better - Rails' whiny_nil support is great for example.

> So many I wouldn't know where to start. Databases have had not-null constraints for years, but an accurate model often means having null values.

These nulls are a different issue than the initialization-originated nulls (that is in fact an other issue with null: they can mean both "I have nothing to put there" and "I forgot to put something there").

> A better improvement would be to handle null references better

No.

> Rails' whiny_nil support is great for example.

whiny_nil exists because you can't do much more in a dynamically typed language, apart from making nil a message sink. Go is not a dynamically typed language, and statically typed language communities have found much better ways to handle most cases where you would use null references decades ago.

Circular data structures where you need to simultaneously initialize two values to each other are the common case where this is problematic. To me, that's a fairly rare edge case and you can easily handle it using mutable variables and option or maybe types.
He mentions BitC. Any comments about this language as I have never heard about it before?
I don't have a lot to say about it personally but it came up fairly often on LtU http://lambda-the-ultimate.org/search/node/bitc
Sorry to nitpick, but one should say

"The language I wish Go were"

The fact that the subjunctive mood really only matters for one verb to me is an equally compelling argument that a) it's not a big deal that people forget and b) people should just remember it.
The link says:

'As a result, appeals to "preserving distinctions" that are "important for communication" and to "avoiding ambiguity" are baseless and indefensible in this case.'

"no matter which form you use, people will understand what you are trying to say."

That is simply not true. Case in point: when I saw the title "The language I wish go was" I expected to find an article about google cancelling the go language.

In general I think he is right but he has to start use D not Go.
Give ooc-lang.org a try ;)
You know what I wish Go had? Compiler-enforced immutable state. It is inexcusable that a language designed for concurrent programming in 2010 doesn't support immutability [1].

[1] http://groups.google.com/group/golang-nuts/browse_thread/thr...

Rob Pike answers this question directly in the last couple minutes of his Go talk at the Emerging Languages Camp, earlier this summer. I think it's pretty illuminating as to the rationale behind why Go does it the way it does.

http://confreaks.net/videos/115-elcamp2010-go

Check out the video, but here's a rough transcription of part of it:

"This is a systems language, which means you should have the choice to do efficient things, if you know what you're doing. You shouldn't be told "you can't do that because you might get it wrong." Let me explain. The model for sharing and locking all that stuff is like having a big piece of paper and we all gather the paper and we make notes on it about who owns what, when -- and we scribble and erase and rewrite and work on it. This model is: you don't have one piece of paper, you have lots of pieces of paper. And you write a message on a piece of paper, and you give him that piece, and he goes away, and you don't have it anymore. He'll bring it back to you if it's relevant to you; he won't if it's not. And because the individual pieces are broken up, there's no concern or worry about who owns what at any one time -- it's whoever's got ahold of it at the moment. And once you understand that, it just doesn't come up as an issue, that for efficiency reasons, I want to pass a pointer on a channel, because once I've passed it I forget it. The only person who has it is the person whose business it is to deal with it now. If you're worried about it and you really care: don't pass a pointer, pass a value. Make it a channel of struct, rather than a channel of pointer to struct. It will be a copy, and there's no way you can share it. But that's your decision, not the language's."

The rationale "You shouldn't be told 'you can't do that because you might get it wrong.'" applied to language design is always suspect. Almost all features added to languages above raw assembler are there to prevent you from doing things that you might get wrong. The flipside of that is having particular syntax that lets you opt in to doing the potentially dangerous thing.

Safe by default, dangerous by responsible choice.

This is essentially relying on convention rather than guarantees. Convention is fine for smaller programs, but it falls down when you're dealing with large, complex code bases worked on by many diverse programmers.

If you're debugging a sharing problem, and relying on convention, you don't even know where to start looking for it in a large codebase.

Yeah, at the time, I found his explanation uncompelling:

which means you should have the choice to do efficient things, if you know what you're doing.

That directly clashes with other design choices in Go. For example, taking the address of a variable on the stack implicitly puts it on the heap (at performance cost) to protect you from accidentally using the pointer after that function returns.

If you're worried about it and you really care: don't pass a pointer, pass a value.

...and pay the performance penalty of doing not just a copy, but a deep copy, simply because the language doesn't have const.

Wonder how the first sentence goes with mandatory garbage collection.
I think CSP is often good but in a system language you need to share stat and with passign pointer you can only share state unsave. So you copy or you share unsave.
> This is a systems language, which means you should have the choice to do efficient things, if you know what you're doing. You shouldn't be told "you can't do that because you might get it wrong.

I don't have the time to watch the video right now, so maybe I'm reading this out of context, but how does compiler-enforced immutability change this? I'm not asking for everything to be immutable (not even in Haskell is that true) -- I'm just asking for the ability to mark an object as immutable and have the compiler make sure that it isn't modified. I'd prefer that non-local objects be immutable by default, but I'm willing to live with mutable by default.

Really, immutable state gives you the efficiency of pointers coupled with the nice semantics of copy-by-value. It's the best of both worlds -- not having it is denying the programmer "the choice to do efficient things".

Someone on the reddit thread argued that because it is CSP you do not need this. The idea being when you send your object down the channel you lose ownership of it. I don't know enough about the Go implementation to know if this is true or not.
I agree completely. I had a note to discuss that (and globals, eww!) but I cut it because honestly that post was too long already.