The language I wish Go was
journal.stuffwithstuff.com
journal.stuffwithstuff.com
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.
Another is that generics already exist in the language - map works even for user defined types. But since you can't make your own generics, map is all you get. With generics map is just a standard library feature rather than a core language feature, and you could write your own containers too if you wanted.
These might not make the language spec strictly shorter, but definitely more orthogonal.
For example, he completely missed defer.
The defer statement is certainly Go-esque, but not as clean as RAII, because I can still forget to write the defer statement after opening a file. This violates DRY when doing file I/O. You can argue that Go never purported to support these concepts in the first place, but, at the end of the day, I'm writing code over and over again needlessly. Not elegant in the slightest.
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.
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.
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?"
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...
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
Broken in what way and what have you read? I'm genuinely interested.
I don't want to end up doing language advocacy for a language I hardly use, but you seem to be confusing error handling with exception handling - something the design of the exception-like mechanism was designed to prevent. As I understand it, in this approach, errors are things you expect to happen (but are errors) and exceptions are things you never anticipated.
What I've read about it so far is the original "Proposal for an exception-like mechanism" thread on the golang-nuts group [1]; and the "Panic, defer and recover" article referenced in the Go FAQ [2]. If any of the stuff below is misinformed, or just plain wrong, I'd love to hear: Go is a language that I want to like.
So now the things I object to:
With Go's model, as I understand it, you can call panic with an arbitrary value. To handle that reliably, your deferred block has to check the type and dispatch to appropriate handler code yourself. That's something you get for free with exceptions, where you can generally provide separate catch blocks for different types of exception.
When you recover from a panic, the execution of your function is over. So if you have two operations which can panic and you want to be sure both are attempted, you have to wrap each one in a separate function. It also means the only way to tell if a function panicked (in case you need to do further cleanup, say) is by checking the return code - and you'd better hope that everyone is following sensible conventions for what they return there.
That leads to another point: Go encourages checking return codes to see if a function failed, whether or not there was a panic. One of the big advances from exceptions, to my mind, was that you no longer had to mingle control flow information with application data. Keeping the two separate makes it easier to write reliable software because it's more self-documenting.
On a more subjective note, I find the syntax really clunky. The examples I've seen all seem to wrap the handler code in an anonymous function: defer func() { ... }() I imagine it would get pretty noisy if you were registering several handlers that way (much like event handlers in Java were).
I don't necessarily think exceptions are perfect either, just that Go's alternative seems like a step backward. But I'd actually quite like to be shown that I'm wrong about this.
[1] http://groups.google.com/group/golang-nuts/browse_thread/thr...; [2] http://blog.golang.org/2010/08/defer-panic-and-recover.html
Control flow in code with "proper exceptions" often becomes as convoluted as code using COMEFROM.
While Go's approach to error handling is not perfect, I find it much clearer and manageable than that in any other language I have used.
That is a massive, damning statement. Please don't make such claims without backing them up. Spreading such ignorance is damaging to the project.
Go's error handling model is simple: handle the error immediately, panic, or ignore it. A panic can be recovered, similar to catching an exception.
Just because you heard someone whining that Go is not like their favourite language doesn't mean you should go around claiming it's "pretty badly broken." What an insult!
Of course if you are trolling, then congratulations on getting a response from me.
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.
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...
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?
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.
And it is ugly. It's ugly because the argument list visually expands across more than one line, including whole statements. The "redundant" forms of "using" and "lock" are both more limited than the closure-passing approach, but their limitations haven't been pressing enough to overcome much of tthe ugliness of the closure-as-parameter approach for this specific case of passing a statement block.
I'm not sure I agree about overloading, though. Maybe something like Haskell's infix notation (1 `add` 2) would be good enough.
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?
A better improvement would be to handle null references better - Rails' whiny_nil support is great for example.
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.
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.
"The language I wish Go were"
'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.
I'm not a native English speaker, but I do think my English is pretty good. Care to explain why the plain past form made you expect that?
In any case, an article titled "Go language cancelled" would have shown up much before anything else. That's what's generally meant by "context".
[1] http://groups.google.com/group/golang-nuts/browse_thread/thr...
Listen to the go talk at Emergin Languages Camp. Rich Hickey asks about that in the end.
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."
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".
Safe by default, dangerous by responsible choice.
C#, for example, doesn't let you modify objects passed in as parameters, unless you highlight that behavior on the method signature and the call itself with the appropriate keywords. There are many other examples in many other languages. "Do what you want, use convention to keep safe" is a recipe for trouble that has caused innumerable very serious defects in software (buffer overflows, security vulnerabilities, etc.) "Do what you want, and you have all these tools to keep track of it" is a much better choice, even if in the end the code does the same exact thing. Banking on humans not making mistakes is not robust.
This should be expressed as a bumper sticker. How about: Human Error: Bet on it!
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.
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.