He forgot to mention how programmers need to add each project directory to $GOPATH to be able to include files stored in project subdirectories. Talk about a braindead idea.
There's no explicit versioning unless you do a fork (which effectively puts the version into the import path). When doing open source work, you just assume that the latest version works. If it doesn't, you can complain and check out an earlier version instead (locally), or in the worst case, fork the code until it's fixed and change your import statements to point to the forked version.
For a company, it makes sense to check your GOPATH into your own source control system, so everyone has the same version of the open source libraries you're using. Then upgrading some open source libraries to a new version is a commit like any other change.
That approach is nice and all if we are dealing with modules which we wish to reuse. If instead we would like to organize our source tree following a deep directory structure, we are forced to screw around with $GOPATH, which is a braindead idea.
Huh? You can literally just set $GOPATH to any directory. The only stipulation is that you put your source code in a 'src' folder. That's literally all you have to do. I'm going to go out on a limb and guess you're one of the people that cries in IRC and can't be bothered to read either of the pages that detail the design and use of the `go` tool.
You keep asserting that this $GOPATH is "braindead" but you provide no meaningful criticism of it or suggest any better alternatives.
I haven't messed with Go, but nothing about the idea of defining a path for source modules sounds braindead on its face to me. I certainly would give the benefit of any doubt to the designers of Go over some random troll.
These are not source modules I'm talking about. If it was only necessary to set $GOPATH to point to source modules then everything would be fine. The problem is that go requires that $GOPATH points to a project's source tree to be able to build it, if the project is organized to store other go source files in subdirectories. We aren't talking about packages or modules intended to be reused in other programs. We are talking about how source code trees are organized.
That's not true. You're supposed to add a workspace path to GOPATH, and keep all your packages in there. You can use multiple workspaces (add more directories to GOPATH), but that's not a requirement.
You started your post by claiming it wasn't true, but you proceeded to say that yes, it is in fact true.
As it is stated in the screencast, a programmer needs to add the workspace to $GOPATH to be able to build the source. That means that for each project being developed in Go, you need to add the path to that project to $GOPATH to be able to build it.
You repeat yourself yet again. How exactly can you claim with a straight face that a workspace doesn't need to be referred to in $GOPATH, and then proceed to claim that you only need to set $GOPATH to a directory which contains those workspaces? You start off your post by claiming it's not true, but then you repeat exactly what I said. Are you joking?
That's not even true. You can use the compiler and linkers to your hearts content.
What you meant to say was, their braindead idea was that if you want to be able to trivially use ANY go program or library on GitHub, you can use $GOPATH and the go tool [1] and not fuck around with autotools and makefiles.
If you're into S&M, no one is stopping you from using the core pieces of the `go` tool by hand.
[1]: ALL of my code can be download, built and run with: `go get github.com/myname/myproject; myproject` (I have $GOPATH/bin on my path)
I'm not talking about gc. I'm talking about go. You know, the program which is extensively marketed as the only program which is required to build and run Go programs, and the standard and default way to deal with Go, from installing third-party packages and running your program through an interpreter.
Referring to gc makes as much sense as referring to gccgo.
What do you want? You don't want to use the `go` tool, DON'T USE IT. Seriously, the alternative that you are implying that you want is already available to you.
Not sure if you know this (or anything about Go), but the go tool just uses a bunch of other programs to accomplish what it does (including git, bzr, the compiler and the linker).
If you want to do your own thing, you're free to ignore the rest of the community in this respect, and create your own build mechanism. In fact, if you pass the "-x" flag to the go tool, you can even see how its accomplishing what its accomplishing; which might help you write this better build tool.
The language has such a limited set of keywords and concepts that I think the issue with having a separate way of creating arrays and channels isn't really a problem. For me the killer idea is that of Go's interfaces ... its really great. And of course the fact it generates a standalone .exe with no dependencies.
> But all the inconsistencies in the language design have made learning it feel like a chore.
That's interesting. Go is one of only two languages that I've been able to learn by writing production code without feeling like I'm getting "held back" by my inexperience (or, on the flip side, holding others back). It never felt like a chore to me; there was only one "gotcha" that caused me a significant amount of frustration.
> So what’s the response from Go’s designers about the requests for nullable strings? To treat the empty string as a special value. Sometimes it’s like they haven’t even read their own design justifications.
The only time I've run across this is with JSON encoding/decoding, but there's a really easy solution to this (take a look at gobson, for example[1])
Yes, make versus new is confusing for beginners and I consider them unfortunate. But fortunately they're easy to avoid most of the time. You can initialize local variables like this:
> I don’t think Go is a terrible language. It has some really great features,...But all the inconsistencies in the language design have made learning it feel like a chore.
In comparison to other languages out there, like Objective-C and C++, this sounds like an i-th world problem.
Objective-C is only very easy to pick up and consistent if you only consider the parts of it that are different from C. The parts of it that it has in common with C can be pretty nasty.
> I feel like Objective-C is very easy to pick up and consistent
I like Objective-C. It's quite wordy, however. The libraries are what's hard to pick up. That, and things like having to copy blocks before you send them off places.
Great! But it might have been more productive to hold off on the blog post until you've been using it for a few weeks, at least. You make a few barbed comments about the language designers, but you are nowhere near familiar enough with Go to be justified in doing so (IMO).
> If Foo is derived from a struct, and it doesn’t work when zero-valued, then there should be a function somewhere called something like NewFoo. Good luck finding the right one.
I've never needed luck, here. This isn't a real problem for actual Go programmers.
> Why is there this special function that construct these three random types?
They're not "random types". They're special built in types that are managed by the language runtime. That makes them distinct from anything you can write as a user, and that's why they use the built in make function for initialization.
> I think channels are value types, which is strange since it’s the only other type created with make.
No, channels are reference types, too. Just like everything created with make.
It would have been good if you had addressed the actual criticisms he made, instead of dismissing the entire post as illegitimate. For example, your point about runtime managed types is technically correct, but not a response to his larger theme of perceived inconsistency and unnecessary deviations.
I could, but I don't really have time to do this for every article along the lines of "I've been using Go for a couple of days and here's what I think..."
When I say "it would have been good" I don't mean in general principle as a means to have orderly debate. I mean: the OP gives some very valid and sensible criticisms (I speak as someone who has used Go off/on for ~2 years), and given your background and area of expertise, I would love to know what the reasons are for some of the puzzling design decisions pointed out.
You seem determined to be dismissive here, which is unfortunate because there are few others on HN who are qualified to address these criticisms.
There's nothing wrong with making a blog post about his early impressions. It's just an opinion on the internet. There's millions more out there, and we all probably disagree with a few of them.
But even aside from that, it's valuable to have someone's early impressions of a language. They may change later. That's cool. But it's also cool to know what they think early on, because it's fairly likely other people will have the same experience. Whether or not his comments represent misconceptions and inaccuracies, they still report his experience. Other people can benefit from knowing others shared the same experience, and if he blogs later when he has more knowledge they can also see how his experience changes over time.
I agree. User experience and interaction design is more than just buttons and animations. It covers all interactions, included programming languages, that's why the post is valid even if it might be inaccurate.
> There's nothing wrong with making a blog post about his early impressions.
I totally agree, but he makes a number of conclusions (or at least strong statements) throughout that, to me, seem premature.
For example, in the introduction: "I get the feeling that Go was designed by System Engineers, who focus on implementation." Systems engineers focus on APIs as much, if not more so, than other programmers. Systems engineers build the things at the bottom of the stack, where API design decisions have a fundamental effect on everything built above them.
sigh Is there really such a need to be so antagonistic in your response?
I mean, I get it, lots of people give Go a hard time, and it must be wearying to see the same arguments dished out again and again (no GC complaint this time, nice right?).
...but surely, when someone makes a post like this that address actual issues they see with the consistency of the language, which aren't the same as the usual run of the mill complaints about GC, generics, etc. a more mature response would be:
Hm, that's interesting. I guess I can see why some
people might think that's a language inconsistency or
issue, but maybe you'll appreciate the Really Smart
Reasons we've chosen to do that when you're more
familiar with Go.
Instead of:
STFU. You don't know what you're talking about.
Is it really that difficult to just be a little bit more humble, and consider that other people may have a valid point or two to make?
This is a complete mischaracterization of what I wrote. I wasn't being antagonistic. At least, it wasn't taken that way by the author of the article: http://news.ycombinator.com/item?id=4930618
> Why is there this special function that construct these three random types? I don’t know.
It's reasonable for someone to be confused about this; my rationale is that these three types are basically where go allows "magic": all of them support special syntaxes (the range operator, optional multi-value context when looking up a map, the select operator on channels), so they have a magic initialiser, ignoring Go's usual "declaration is initialisation" rule.
As others have said, it's not too bad a special case in the context of the language. The problem I have with it is that it introduces the possibility of run-time errors that in a language this advanced and generally so well-designed should probably be compiled-time errors: panics when you assign to keys in nil maps or deadlocks when you send to a channel you forgot to initialise are both errors of a class that the language generally doesn't have.
It's worth noting that some of the Go inconsistencies derive from the language designers desire to make the grammar regular (facilitating extremely efficient and simple parsing). In order to do this, you need statements that mean different things to the compiler to look different. This manifests itself in the (I admit, very inconsistent) var x int foo vs. x := 5 declaration. They mean the same thing to you, but different things to the compiler -- to preserve a regular grammar, they have to look different.
It's definitely a little ironic that what is needed to make a regular grammar makes the language more inconsistent.
I suspect the grammar issues could have been worked out in a more natural and consistent manner.
E.g., instead of a special ":=" declare-with-auto-type+assign operator, just use ":" in all type declarations, and make the type optional in initialized declarations. Then the following would all be equivalent:
var x : int; x = 5
var x : int = 5
var x := 5
Using ":" in declarations would also make them more readable, and more consistent with most other languages using this type of declaration syntax. [If you've read my other comments about Go on HN, this is a pet peeve of mine... :]
Go's syntax does feel pretty sloppy, like it evolved under a variety of different authors and directions, and never really got a cleanup pass in the end...
Supporting declaring variables with new(), make and local variable assignment isn't mandated by the need to make the grammar regular. It's simply a braindead idea. Go might have significant improvements over C, but they screwed the pooch on this one.
So, when faced with criticism mentioned in the blog post and referred to in a post, instead of thinking about it you mindlessly insult others and say absolutely nothing about what has been said. Who exactly is the troll here?
> The standard library also seems to be missing a lot of the most common data types. As far as I can tell, there are no standard stack or queue types. ... How can a language provide an http server, but no stack type?
I've never really done any Go, but aren't these just different use cases for a Slice? Why would they need their own data type?
You are absolutely correct. Indeed, it is rather trivial to do so.
Pushing onto a stack is equivalent to `s = append(s, obj)`.
Checking if the stack is empty is equivalent to `len(s) == 0`.
Peeking at the top (assuming its non-empty) is equivalent to `top := s[len(s)-1]`.
Popping from the top (assuming its non-empty) is equivalent to `s = s[:len(s)-1]`
Because, given a common interface shared between stacks and queue types, you can turn a depth-first search algorithm into a breadth-first one, simply by changing the parameter from stack to queue.
I'll just summarize my ramble with: the author of this blog post could have at least read the short Spec or "Effective Go". Would have dispelled his confusion on several things and would have prevented a few of the inaccuracies.
however, that doesn't necessarily mean that go is "a bad language". it's more a cultural thing, as far as i can see.
go has some nice ideas and is a big advance over, say c or c++. it's also made by people with a lot of experience and, for want of a better phrase "good taste".
but it does have a certain culture (or, if you like, a disregard for a certain culture).
nothing very useful seems to have come from these observations, and many people have made them. it's probably better to simply consider the language as a tool and work with it on its own terms. perhaps the most you can say is that, because it does choose its own path, you really do need to use it in the way intended by the authors.
I was interested in Go because of its brevity, speed, and because Rob Pike wrote it.
Reading this I see a lot of the reasons I like Ruby: no worrying about different constructors or needing to worry excessively about types. If only Ruby treated everything, even the little bit that is syntax, as mutable objects, it would be near-perfect (and it would basically be Lisp). But, it's close enough.
Having done a bit of development in both, I found I got productive in Go quicker than with Ruby. With Ruby, there's many ways to do the same thing. That might be great when you're experienced with the language, but for someone starting out it can be daunting. The compexity of installing RVM put me off also - I was never able to install it on Linux properly so ended up using the version that came with the Ubuntu package manager, which was an old version.
You don't have to install RVM, but it does help. It was never that difficult for me, but I use OS X for Ruby dev mostly.
And there may be a variety of ways of doing things, but that's because (1) Ruby has been more well-used than Go, and (2) it has more flexible loops, etc. to make it less verbose, and yes there are a variety of ways to add methods, attributes, etc. but after you spend a little time to understand how the Ruby object model works, you'll understand why- almost everything is an object. Also, the language has evolved, so there are some differences in syntax between 1.8 and 1.9 but you can still use 1.8 syntax in 1.9.
Yeah agreed Ruby is more powerful especially with loops. The object model and that methods are messages passed between objects is also great, once you understand it.
I really like Go. My team has been working on a Go application at work for the last two months and I've had a great time of it.
That said, like all good languages, it's perfect until you run into the small list of warts that enrage you. In particular, as the author said, the fact that the make() types are different can get pretty annoying. What's really going on there are that make is really the only generic function in Go. That's why it's special -- unlike a lot of Go builtins, it's impossible to write make() in Go. I really do think this is the biggest single wart in the language (although there are others).
I don't really agree with the author's complaints about declaration syntax. I don't find it distracting: just figure out an idiomatic way to do things and move on.
Not having nil-able strings is annoying though. There are plenty of functions both in the standard library and in my own code that return ("", error) simply because the first return type is a string -- in a similar function with a different return type, I'd just return (nil, error) and be done. Not a big deal but it is a wart.
There are also some definite problems with the standard library, but I expect these will get fixed by OSS packages in time.
Before we all dump on Go though, it's worth pointing out some of the really cool innovations in the language that work great:
* Dependency management in the code is awesome. I love that importing a package and not using it is a compilation error. This seems to be so successful that they went and made declaring unused variables an error as well, which also does wonders for readability.
* Once you understand how you're supposed to use it, the GOPATH story is pretty great. Package management can get tricky and it's cool to see a language attacking it head on from the beginning -- you don't want to end up like Ruby.
* The notion of a Go "package" for namespacing -- and the exporting of CapitalizedVariables but not uncapitalizedVariables makes for very readable code. I really like it.
* The multiple return types for error handling definitely makes for more verbose code than the try-catch-finally pattern, but it also feels a bit cleaner. It takes a while to fully see the benefits of it, but it works great in the end.
I'm really excited for the future of this language. I think with a few more years, a few better frameworks, etc, we'll be able to do some really cool stuff very very easily in Go.
But is it that make is inherently confusing or that "creating" things in general is muddied by being conceptually too crowded? There are at least 6 concepts jostling around (make, new, zero values, special reference types, literals, pointers-to-literals). For starters, it seems like that could have have been reduced to 5 by eliminating new(T) completely and allowing the already-idiomatic &T{} to pick up the slack.
I must agree with your overall praise for the language. The package/export system and the official tooling is masterfully done!
Nullable types? I thought that was one of the bad things about C#. I asked Anders Hejlsberg about it when he was in the country, and he said if he could go back, he would remove nullable types from C#.
Why can't you add methods to types outside of your package boundaries? Because Go aims for clean, comprehensible, robust boundaries between components: http://talks.golang.org/2012/splash.article
60 comments
[ 2.4 ms ] story [ 133 ms ] threadGo code is nothing if not explicit.
For a company, it makes sense to check your GOPATH into your own source control system, so everyone has the same version of the open source libraries you're using. Then upgrading some open source libraries to a new version is a commit like any other change.
I haven't messed with Go, but nothing about the idea of defining a path for source modules sounds braindead on its face to me. I certainly would give the benefit of any doubt to the designers of Go over some random troll.
How is that different than the /I ${INCLUDE} path of C/C++ compilers (and many other languages)?
This screencast might help: http://www.youtube.com/watch?v=XCsL89YtqCs
As it is stated in the screencast, a programmer needs to add the workspace to $GOPATH to be able to build the source. That means that for each project being developed in Go, you need to add the path to that project to $GOPATH to be able to build it.
That is a very poor way to do develop software.
Personally, I keep 2; one for external packages, and one for ones I'm working on.
What you meant to say was, their braindead idea was that if you want to be able to trivially use ANY go program or library on GitHub, you can use $GOPATH and the go tool [1] and not fuck around with autotools and makefiles.
If you're into S&M, no one is stopping you from using the core pieces of the `go` tool by hand.
[1]: ALL of my code can be download, built and run with: `go get github.com/myname/myproject; myproject` (I have $GOPATH/bin on my path)
Referring to gc makes as much sense as referring to gccgo.
If you want to do your own thing, you're free to ignore the rest of the community in this respect, and create your own build mechanism. In fact, if you pass the "-x" flag to the go tool, you can even see how its accomplishing what its accomplishing; which might help you write this better build tool.
That's interesting. Go is one of only two languages that I've been able to learn by writing production code without feeling like I'm getting "held back" by my inexperience (or, on the flip side, holding others back). It never felt like a chore to me; there was only one "gotcha" that caused me a significant amount of frustration.
> So what’s the response from Go’s designers about the requests for nullable strings? To treat the empty string as a special value. Sometimes it’s like they haven’t even read their own design justifications.
The only time I've run across this is with JSON encoding/decoding, but there's a really easy solution to this (take a look at gobson, for example[1])
[1] http://labix.org/gobson
foo := map[string]string {}
bar := []string {}
In comparison to other languages out there, like Objective-C and C++, this sounds like an i-th world problem.
Though I felt the same way about Go, I may just be terrible at noticing things like this.
i as in i^2 = -1.
> I feel like Objective-C is very easy to pick up and consistent
I like Objective-C. It's quite wordy, however. The libraries are what's hard to pick up. That, and things like having to copy blocks before you send them off places.
Great! But it might have been more productive to hold off on the blog post until you've been using it for a few weeks, at least. You make a few barbed comments about the language designers, but you are nowhere near familiar enough with Go to be justified in doing so (IMO).
> If Foo is derived from a struct, and it doesn’t work when zero-valued, then there should be a function somewhere called something like NewFoo. Good luck finding the right one.
I've never needed luck, here. This isn't a real problem for actual Go programmers.
> Why is there this special function that construct these three random types?
They're not "random types". They're special built in types that are managed by the language runtime. That makes them distinct from anything you can write as a user, and that's why they use the built in make function for initialization.
> I think channels are value types, which is strange since it’s the only other type created with make.
No, channels are reference types, too. Just like everything created with make.
There are more misconceptions and inaccuracies.
Haha, I agree. I wrote it mostly to try to get my thoughts straight, and didn't expect it to be posted to Hacker News. :)
But my criticisms are that these things make Go hard to learn. It may be a great language once you get everything straight.
> No, channels are reference types, too.
Ah, good to know. That wasn't clear to me from my first reading of the docs, but you're definitely right.
Might be a good idea to continue honing that post. It is already useful and could be much more so.
This article written by Rob Pike covers the reasoning behind many design decisions: http://talks.golang.org/2012/splash.article
You seem determined to be dismissive here, which is unfortunate because there are few others on HN who are qualified to address these criticisms.
But even aside from that, it's valuable to have someone's early impressions of a language. They may change later. That's cool. But it's also cool to know what they think early on, because it's fairly likely other people will have the same experience. Whether or not his comments represent misconceptions and inaccuracies, they still report his experience. Other people can benefit from knowing others shared the same experience, and if he blogs later when he has more knowledge they can also see how his experience changes over time.
I encourage more blogging of this sort.
I totally agree, but he makes a number of conclusions (or at least strong statements) throughout that, to me, seem premature.
For example, in the introduction: "I get the feeling that Go was designed by System Engineers, who focus on implementation." Systems engineers focus on APIs as much, if not more so, than other programmers. Systems engineers build the things at the bottom of the stack, where API design decisions have a fundamental effect on everything built above them.
It's definitely worth reading this: http://talks.golang.org/2012/splash.article
I mean, I get it, lots of people give Go a hard time, and it must be wearying to see the same arguments dished out again and again (no GC complaint this time, nice right?).
...but surely, when someone makes a post like this that address actual issues they see with the consistency of the language, which aren't the same as the usual run of the mill complaints about GC, generics, etc. a more mature response would be:
Instead of: Is it really that difficult to just be a little bit more humble, and consider that other people may have a valid point or two to make?This is a complete mischaracterization of what I wrote. I wasn't being antagonistic. At least, it wasn't taken that way by the author of the article: http://news.ycombinator.com/item?id=4930618
It's reasonable for someone to be confused about this; my rationale is that these three types are basically where go allows "magic": all of them support special syntaxes (the range operator, optional multi-value context when looking up a map, the select operator on channels), so they have a magic initialiser, ignoring Go's usual "declaration is initialisation" rule.
As others have said, it's not too bad a special case in the context of the language. The problem I have with it is that it introduces the possibility of run-time errors that in a language this advanced and generally so well-designed should probably be compiled-time errors: panics when you assign to keys in nil maps or deadlocks when you send to a channel you forgot to initialise are both errors of a class that the language generally doesn't have.
It's definitely a little ironic that what is needed to make a regular grammar makes the language more inconsistent.
E.g., instead of a special ":=" declare-with-auto-type+assign operator, just use ":" in all type declarations, and make the type optional in initialized declarations. Then the following would all be equivalent:
Using ":" in declarations would also make them more readable, and more consistent with most other languages using this type of declaration syntax. [If you've read my other comments about Go on HN, this is a pet peeve of mine... :]Go's syntax does feel pretty sloppy, like it evolved under a variety of different authors and directions, and never really got a cleanup pass in the end...
The difference between make and new is well covered in documentation and on the mailing list.
Do I literally need to link you to the FAQ entry for "make vs new"? Or the docs for initialization? You're the one spreading FUD.
FYI, the grammar is context-free, not regular.
I've never really done any Go, but aren't these just different use cases for a Slice? Why would they need their own data type?
Pushing onto a stack is equivalent to `s = append(s, obj)`. Checking if the stack is empty is equivalent to `len(s) == 0`. Peeking at the top (assuming its non-empty) is equivalent to `top := s[len(s)-1]`. Popping from the top (assuming its non-empty) is equivalent to `s = s[:len(s)-1]`
however, that doesn't necessarily mean that go is "a bad language". it's more a cultural thing, as far as i can see.
go has some nice ideas and is a big advance over, say c or c++. it's also made by people with a lot of experience and, for want of a better phrase "good taste".
but it does have a certain culture (or, if you like, a disregard for a certain culture).
nothing very useful seems to have come from these observations, and many people have made them. it's probably better to simply consider the language as a tool and work with it on its own terms. perhaps the most you can say is that, because it does choose its own path, you really do need to use it in the way intended by the authors.
Reading this I see a lot of the reasons I like Ruby: no worrying about different constructors or needing to worry excessively about types. If only Ruby treated everything, even the little bit that is syntax, as mutable objects, it would be near-perfect (and it would basically be Lisp). But, it's close enough.
Maybe if Go could become Ruby...
And there may be a variety of ways of doing things, but that's because (1) Ruby has been more well-used than Go, and (2) it has more flexible loops, etc. to make it less verbose, and yes there are a variety of ways to add methods, attributes, etc. but after you spend a little time to understand how the Ruby object model works, you'll understand why- almost everything is an object. Also, the language has evolved, so there are some differences in syntax between 1.8 and 1.9 but you can still use 1.8 syntax in 1.9.
That said, like all good languages, it's perfect until you run into the small list of warts that enrage you. In particular, as the author said, the fact that the make() types are different can get pretty annoying. What's really going on there are that make is really the only generic function in Go. That's why it's special -- unlike a lot of Go builtins, it's impossible to write make() in Go. I really do think this is the biggest single wart in the language (although there are others).
I don't really agree with the author's complaints about declaration syntax. I don't find it distracting: just figure out an idiomatic way to do things and move on.
Not having nil-able strings is annoying though. There are plenty of functions both in the standard library and in my own code that return ("", error) simply because the first return type is a string -- in a similar function with a different return type, I'd just return (nil, error) and be done. Not a big deal but it is a wart.
There are also some definite problems with the standard library, but I expect these will get fixed by OSS packages in time.
Before we all dump on Go though, it's worth pointing out some of the really cool innovations in the language that work great:
* Dependency management in the code is awesome. I love that importing a package and not using it is a compilation error. This seems to be so successful that they went and made declaring unused variables an error as well, which also does wonders for readability.
* Once you understand how you're supposed to use it, the GOPATH story is pretty great. Package management can get tricky and it's cool to see a language attacking it head on from the beginning -- you don't want to end up like Ruby.
* The notion of a Go "package" for namespacing -- and the exporting of CapitalizedVariables but not uncapitalizedVariables makes for very readable code. I really like it.
* The multiple return types for error handling definitely makes for more verbose code than the try-catch-finally pattern, but it also feels a bit cleaner. It takes a while to fully see the benefits of it, but it works great in the end.
I'm really excited for the future of this language. I think with a few more years, a few better frameworks, etc, we'll be able to do some really cool stuff very very easily in Go.
I must agree with your overall praise for the language. The package/export system and the official tooling is masterfully done!
http://en.wikipedia.org/wiki/Option_type
Why can't you add methods to types outside of your package boundaries? Because Go aims for clean, comprehensible, robust boundaries between components: http://talks.golang.org/2012/splash.article