60 comments

[ 2.4 ms ] story [ 133 ms ] thread
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.
I interpret this as encouragement to use fully-qualified package names (eg: https://github.com/burke/zeus/blob/master/go/zeusmaster/zeus...), which I don't really have a problem with.

Go code is nothing if not explicit.

How do you manage dependency versions with a fully-qualified package name?
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.
The problem is that go requires that $GOPATH points to a project's source tree to be able to build it

How is that different than the /I ${INCLUDE} path of C/C++ compilers (and many other languages)?

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.

This screencast might help: http://www.youtube.com/watch?v=XCsL89YtqCs

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.

That is a very poor way to do develop software.

Or, you could have multiple projects in a single workspace...

Personally, I keep 2; one for external packages, and one for ones I'm working on.

No, that's not true. You just keep all your projects in a single workspace (as I do), and their namespaces keep them separate.
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])

[1] http://labix.org/gobson

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:

foo := map[string]string {}

bar := []string {}

> 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.

I'm not sure what you mean, maybe it is Apple's tools and Cocoa but I feel like Objective-C is very easy to pick up and consistent.

Though I felt the same way about Go, I may just be terrible at noticing things like this.

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'm not sure what you mean

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.

> I’ve been learning Go the last few days.

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.

> you are nowhere near familiar enough with Go to be justified in doing so

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.

> 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.

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..."

This article written by Rob Pike covers the reasoning behind many design decisions: http://talks.golang.org/2012/splash.article

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 encourage more blogging of this sort.

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.

It's definitely worth reading this: http://talks.golang.org/2012/splash.article

He's been using Go much longer than probably 99.9% of those who are critical of (insert language here)
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?
"STFU. You don't know what you're talking about."

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

For constructors, why notabstract that away all the inconsistencies with a function call. Such as

    Foo foo_module_new() ?
I don't know Go, but I would do that in C for example and just create an opaque struct for example.
> 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.
Another uninformed comment, I can't freaking believe you created a troll account just to spread your ignorance about Go.

The difference between make and new is well covered in documentation and on the mailing list.

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?
What do you want me to say? Everything in the blog article is addressed in the two very short documents that have been mentioned all over here.

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.

> It's worth noting that some of the Go inconsistencies derive from the language designers desire to make the grammar regular

FYI, the grammar is context-free, not regular.

> 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.
your general impression is correct, fwiw.

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.

Maybe if Go could become Ruby...

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#.