top | item 7015145

Go’s Type System Is An Embarrassment

109 points| mikeevans | 12 years ago |functionwhatwhat.com | reply

109 comments

order
[+] colin_mccabe|12 years ago|reply
What's embarrassing is that people are still evaluating programming languages like they were bags of features. The more features you stuff in the bag, the better it must be!

The reality is that generics aren't free. They result in difficult-to-understand error messages and more cognitive complexity.

To quote Yossi Krenin, "You may think it's a StringToStringMap, but only until the tools enlighten you - it's actually more of a... std::map<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >"

Of course, it would be nice to have generics, or something like them, for writing typesafe containers. But there are other goals in the language like fast compilation, easy-to-understand error messages, and overall simplicity. I hope they can come up with an implementation of generics that can keep those good properties of the language. But if they can't, it shouldn't be added just to get a feature bullet point. Use the right tool for the job.

[+] kerkeslager|12 years ago|reply
> The reality is that generics aren't free. They result in difficult-to-understand error messages and more cognitive complexity.

> To quote Yossi Krenin, "You may think it's a StringToStringMap, but only until the tools enlighten you - it's actually more of a... std::map<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::less<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<std::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >"

The Kreinin quote is in relation to C++. For historical reasons (reverse compatibility with C) and interoperability with other parts of the language (manual memory management), C++ types are way more complicated than types in a more recent language such as C#, where a Map<string,string> is really not much more complicated than you would think it would be. One might be able to argue that generics make things more complicated, but they certainly don't have to make them as complicated as C++ does.

[+] alok-g|12 years ago|reply
I am not convinced that C++ examples are valid as a generalization of the failures of generics for other languages. C++ libraries are often overly generalized with means to "configure" the generics by passing additional type arguments. I do not think it has to be like that.
[+] stinos|12 years ago|reply
What's embarrassing is that people are still evaluating programming languages like they were bags of features. The more features you stuff in the bag, the better it must be!

well in some cases your platform only support one language, or maybe to. So it makes a lot of sense to then evaluate tha language as a whole. Even then, I don't see much wrong with evalulating a language as abag of features as long as you don't simply consider the amount of features but rather it's quality.

The reality is that generics aren't free.

No, but I noticed that the end, once you know how to use them properly and become used to them, they tend to become invaluable, even to a point that programmers coming from years of C++ background are struggling in other languages when they lack generics. Sure, right tool for the job. But in case of generics I'd say they're much too broad to speak of a single tool and they can be used for multiple jobs. So I'm with the author and feel go lacks on that point.

To quote Yossi Krenin

aka the guy who doesn't want to write std::map< std::string, std::string > :P

[+] codygman|12 years ago|reply
Please see the simple implementations of generics in ocaml and C# (added later).
[+] nostrademons|12 years ago|reply
My biggest beef with the Go typesystem is that they didn't get rid of nil. Tony Hoare (the guy who invented null) has acknowledged they were his "billion dollar mistake" [1], common practice in Java and Haskell is moving away from them, and yet Go included them anyway - in a language that's supposed to be robust because you're supposed to handle every case. The Maybe (or @Nullable) type is a much better idea, because it flips the default from "any variable may be null" to "only variables explicitly declared as such may be null".

[1] http://www.infoq.com/presentations/Null-References-The-Billi...

[+] frou_dh|12 years ago|reply
It's exacerbated by nil being reused as a value for things that aren't even pointer types, like the slices, channels and especially interface "pairs"[1]

[1] (<NoType> . nil) vs (T . nil) is pretty weird: http://play.golang.org/p/fmk-72OYkO

[+] kkowalczyk|12 years ago|reply
Hoare didn't invent 0-valued pointer. It was there since the beginning of time. Or at least the beginning of the CPUs.

People talk about getting rid of nil like it's actually possible.

It's not.

If you have pointers, they sometimes have to start the life uninitialized (i.e. with a value of 0) hence nil pointers.

As you admit yourself, the proposed solutions don't actually get rid of anything. At best they can force you to handle nil value by wrapping it in some wrapper.

Guess what - if you have that kind of discipline, you can do the same in C++.

Why aren't people doing that?

Because it comes at a cost. There's a cost to the wrapper and when it comes down to it, you still have to write the code to handle the nil pointer whether you're using a wrapper or a raw pointer.

It just doesn't buy you much.

Finally, fixing crashes caused by referencing a null pointer is downright trivial. Spend a few weeks chasing a memory corruption caused by multi-threading and you'll come to a conclusion that fixing a null pointer crashes (which can be done just by looking at the crashing callstack most of the time) is not such a big deal after all.

[+] AYBABTME|12 years ago|reply
In 97.5% of case, the `nil` value of Go types follows the NullObject pattern.

There's really just nil pointers that are still equivalent to `null`, and their absence would be an abnormality, as there's no reason why a pointer couldn't have a nil representation.

[+] frou_dh|12 years ago|reply
This masterclass in clickbait titling pulled in 735 comments and counting on /r/programming. Watch and learn, bloggers!
[+] dkuntz2|12 years ago|reply
It did something similar here before the outage.
[+] thinxer|12 years ago|reply
What's wrong with leaking un-exported element? Your code wants it to be exported. If you care about it, please return an interface type.
[+] rfw|12 years ago|reply
As I posted before the outage, I don't think leaking un-exported elements is actually that broken either -- Haskell gets by fine with it (you can omit the type and its constructor in the module export list and have types "leak" into the code importing said module). It can be a useful way to keep types opaque and not allow users to construct instances directly.

The actual issue, in my opinion, is the weird behavior of the reflect package which allegedly cannot reflect on the leaked element despite its members being publicly accessible anyway.

[+] vectorpush|12 years ago|reply
I love go, but my personal gripe with the type system is that there is no way to pass type information around without using a zero valued (or fully populated, it makes no difference) instance. For example, I have a factory struct with a New method that returns interface{MySignatures()}, but I cannot inform the factory of the concrete type I want it to produce without passing in something like new(ConcreteStructWithMySignatures). Some have suggested I pass in a string, but what is the point of a type system if I have to use strings to reason about my types?
[+] kristianp|12 years ago|reply
Go is designed to be a small, simple language, it's not hard to release that. If you want a fully featured type system, go program in Ocaml, Haskell or another 'big' language.
[+] TylerE|12 years ago|reply
That falls flat on it's face as a cop out when you look at a language like nimrod that manages to be practically as simple as go while including full generics. Oh, it's faster too.

http://nimrod-lang.org/tut2.html#generics

[+] lucian1900|12 years ago|reply
Both ML and Haskell are really tiny languages syntactically and have very simple core type systems.

Go has both crappy syntax (excusable for appealing to C programmers) and a pathetic type system.

[+] namelezz|12 years ago|reply
Maybe Go is a small and simple language now because it's still missing some features that potentially make it a complicated language. :)
[+] letstryagain|12 years ago|reply
Are we complaining about generics again?
[+] alok-g|12 years ago|reply
I see nothing wrong with that. I would like to keep on complaining till they listen! :-)
[+] bsaul|12 years ago|reply
Type system power really is the only question i have left relative to go. I've always seen go being used for low level very technical stuff, where you don't need lots of abstraction, but rather powerful primitives and libs. I've always wondered how it scales to business process modeling, and the lack of generics always worried me.

On another aspect, 2013 was the year of Go being unanimously praised, and i've got the feeling 2014 is going to be the year of the backlash.

Still, i don't see any other languages able to take the crown back. Rust looks way too unpolished and has not only one but multiple pointer types.

[+] TheCoelacanth|12 years ago|reply
Rust really doesn't target the same uses that Go targets. Go basically targets people who need a faster Python or a simpler Java. It doesn't have enough low-level control to be used for most of the things that C is still used for.

Rust targets the low-level uses where a high degree of control over memory management is necessary. One of it's main goals is to make it possible to statically reason about memory ownership.

You would never try to implement something like a modern browser or a production-quality language runtime in Go. On the other hand, Rust is targeted directly at that kind of thing, and the multiple pointer types are absolutely necessary for those use cases.

[+] pcwalton|12 years ago|reply
> Rust looks way too unpolished and has not only one but multiple pointer types.

This is overstated. There is just one pointer type in the language, the unique pointer, and there are also references.

That said, the distinction between unique pointers and references is there for a reason. You need this power for low-level programming. A pervasive global GC is not appropriate for all applications.

[+] pkroll|12 years ago|reply
Nothing I've seen on Hacker News would lead me to the conclusion that 2013 was the year of Go being unanimously praised. It gets flack every time a story shows up.
[+] puppetmaster3|12 years ago|reply
Are we trying to make Go into Java or JavaScript as it relates to dynamic or static?