You are viewing bramcohen

Fri, Nov. 13th, 2009, 10:08 pm
Comments on Go

Here are my preliminary thoughts on the Go programming language.

The most interesting feature for me personally is the built-in threading. Aside from its superb support for multi-core, it's just plain a good set of ways of doing networking. The lack of a decent built-in networking library (and generally coordination library) in Python has been a thorn in my side just about forever. In particular the promotion of queues to being one of the few built-in primitives with their own special syntax encourages good threading practice and is clearly warranted. Even such a simple command as 'wait until either the UI thread or the networking thread comes up with something' is a source of ongoing pain in most languages, but is built into Go as a core concept using select.

Go seems to finally get the static typing problem solved. Its := operator is a reasonable middle point between C++'s ludicrous verbosity and ML's excessive magic. Types being structural is also a huge win. There's no end of stupid architectural haggling over what module a base type sits in and who owns it, and the answer 'nowhere' completely gets rid of that problem. It seems to me that there are deep subtle problems with such declarations - for example, how does it statically check that the parameters accepted by methods of a type you're receiving are compatible with what you want to pass them? But maybe I just haven't thought about it enough. It's too bad that Go doesn't currently have generics. I for one won't start any new project in it until it reaches that level of maturity.

Go's lack of exception handling is a real problem, and another thing I'm blocking on to do real development in it. My own preferred method for adding it would be that if you call a function which has multiple return values and you don't handle one of them, it defaults to shorting to the same return of the caller, although some people might complain about that being too much like Java's 'throws'. That said, I've gotten so used to debugging by stack trace that I'd be loathe to not have stack building built into the language in some form, and in fact I've gotten really attached to a tricked out logging tool I wrote which can decorate any object and automatically logs a full stack trace of every assignment which is made to the object and allows you to print them all out at once. But perhaps such trickery is really the domain of highly dynamic languages, and not appropriate for something as low level and performance oriented as Go.

The primitives in Go are quite good. All languages should have special maps and lists built in. I think it actually doesn't go far enough with giving them special status, and should have Python-style special syntax for maps. The curly brackets could be freed up by simply eliminating their current usage and making formatting have syntax. It's more than a little bit absurd that the language designers themselves have a setup where a utility standardizes the formatting of their own code every time they commit, but they still maintain the nominal free-form nature of the language. Really guys, I know you were traumatized by Fortran's original awful enforced formatting, but that was a long time ago and it's time to let go.

That said, the primitives are given too much special status in other ways - they're the only things which have type parameterization, making it impossible to even implement their interfaces yourself, and worse, they're the only things which are call by reference. The call by reference thing worries me a lot. I really, really don't want Go to become the reference/pointer mix hell which C++ has become, but it's already headed in that direction. It really shouldn't matter that much - things which are passed are either an address or a copy, and the reference/pointer distinction really just has to do with what's the default (okay, so typically references don't let you overwrite either, but that's not a fundamental property). I for one strongly prefer the default be an address, and clearly when push comes to shove Go's designers do too, but more important than which way it is is that it should be consistent. Already transitioning to something consistent might require rewriting huge amounts of code, and it's getting worse, so fixing this problem might have to happen soon or never, and I'm afraid that it might already be never.

Go's speed of compilation is very nice, although I'm afraid I view that not so much as a strength of Go but as an awfulness of C++. Why C++ continues to take forever to compile even on machines many orders of magnitude faster than the first ones I ever used it on has long been a mystery to me. I hope the answer is simply that it's a language which wasn't designed with ease of parsing in mind, and has a whole layer of preprocessing on top of it which is horribly abused.

It's interesting that Go is going the garbage-collected route. If such a low-level language as Go can get away with that (and, truth be known, their preferred garbage collector isn't really integrated yet, so it's a little early to call it) then we may never see another non-garbage-collected language ever again.

I despise the use of initial capital letters to specify that something is public. Maybe if I used it for a while I'd learn to not hate it, but for now I hate it. Does chinese even have uppercase?

It's entirely possible that after using Go for a while something else would really start to gnaw at me about it, but it generally has a good smell, so hopefully not.

If you've read this far, you should follow me on Twitter.

Mon, Nov. 30th, 2009 01:48 pm (UTC)
peter_geoghegan

The reason that C++ takes so long to compile is because it allows you to instantiate classes on the stack (as against instantiating a mere pointer to the class) - the size of the class must be known at compile time, so you must give the compiler the classes' definition, as well as any class definitions on which the class is dependent on in turn, and so on. C++ programmers endevaour to minimise compilation dependencies, by the use of declarations when a full definition is unneeded (such as when you just have a pointer to the class, or are just passing it as a parameter to a function that is defined elsewhere). There is actually an idiom called "pimpl" or "chesire cat" which entails creating a facade, forwarding class that just has the public interface of the implementation, to which it is the only client - this is something I'd only use in extreme cases though. Slow compile times are a weakness of C++, to be sure.

Have you developed in C++ (as against just compiling a C++ program)? Typically, you have a debug build without compiler optimisations, which tends to speed things up considerably, especially if you're using GCC.

I don't know about Chinese, but Hebrew doesn't have uppercase or lowercase.

I'm not sure why the whole reference/pointer thing causes confusion/annoyance; references are just safer alternatives to pointers, in that they must be initialised (cannot be NULL or wild), and cannot change that which they refer to - they're an "alternative name" for the variable, not a distinct thing - they have the same memory address as the original, referenced variable (at least ostensibly). They exist to facilitate easy passing by reference to functions (no need to use pointer semantics), and to faciliate operator overloading (no need to de-reference a pointer returned by an operator, defeating the point of overloading - exploiting client's intuition about what the operator ought to mean).