Is this an old presentation? Doesn't Go now start with 8KB stacks per goroutine, which is the same as OS level threads (which physically allocate 8KB from a larger virtual mapping)?
I thought the benefits of goroutines were now about more efficient scheduling, not lower memory utilization?
Go 1.3 will ship with contiguous stacks that start at 8kB. The original plan was to cut them back at 4kB, but it was not feasible ritgh now (while maintaing performance). It will probably be reduced in the future.
No, threads don't use 8kB stacks, they use 2-8MB stacks.
A particular point of goroutines is that they use less address space than threads. Both goroutines and threads use roughly the same amount of physical memory.
Yet another piece of Go advocacy from someone who seems to have never heard of any modern functional languages. If the only alternatives were perl/python/ruby/javascript/java/C then Go would probably be a good choice for some problems. But what about OCaml, F#, Haskell or Scala?
He mentions both Haskell and C#, but I agree with your overall point. Nimrod, Rust, D, C++11 and Julia are also missing from the comparison. (and more)
Well, I understand your point, but there's a limit to how much namedropping you can do in one article. There are hundreds of programming languages, each with their own little niche of enthusiasts ready to get outraged that their raison de vivre language hasn't been mentioned. The fact that Nimrod, D or Julia weren't mentioned isn't necessarily relevant in the overall estimation of the quality of the contents of the article.
Rust is not a stable language, so no long running project can safely use it. Is Julia a stable language with backwards compatibility? It's v0.2.1 right now, which doesn't fill me with confidence. He mentions C++, pretty sure he knows about C++11. And yeah, you can only drop so many names.
A lot of FP people tend to think people don't use FP are those who have "never heard of any modern functional languages". I don't know where does this kind of arrogance come from.
Sure, functional language can be used in almost all the programming related projects. But are they really a good fit or not is not decided by FP advocacy, but the people who actually tackle these problems.
Go for now shines in a pretty niche market, it is extremely good fit for this niche market, and this fact is not advocated by Go fans or Go team, but by large amount of open source projects and emerging usage in quite a few serious companies/startups.
You can certainly go ahead and ask them "Why not use OCaml, F#, Haskell or Scala", or you can use FP to make a better project in this field to show people FP is indeed much better fit.
To some of us, programming language is merely a tool to solve a problem, not to show off the shining design concept. For us, Go hits a incredibly sweet point on the balance of coding efficiency and running efficiency. Note that, coding efficiency has nothing to do with "how beautiful and elegant this code snippet is", it has everything to do with "if this project is easy to read, easy to maintain and easy to expand"
What's the niche Go occupies and what makes go better than those other languages you mentioned for that niche?
Those languages are better tools for solving problems in a lot of cases, the problem is people just choose tools that are similar to ones they already know rather than the best tools available.
I ported a game server from Clojure to Go. Go was simply a better fit. When you need to make sure that memory in structs is laid out a certain way to avoid cache line clobbering with high parallelism and ensure arithmetic is using 64 bit ints, Go does this more easily. It would have the same advantages over Scala. Not sure about Haskell or OCaml. (Suspect not.)
You don't even know what kind of project I'm talking about and you are saying "rather than the best tools available"? I don't get it. Do you really believe that problem field doesn't matter, and FP is "best tools available by default" for any field anyway?
Go to github and see the rank of project written in Go, you will see clearly what is the niche market(not that niche I would say) I'm talking about.
> A lot of FP people tend to think people don't use FP are those who have "never heard of any modern functional languages".
A lot of the slides are about why he wouldn't use Perl, why he wouldn't use Java, and the like. His whole argument is that there's no other language in the efficient / nice to program quadrant. To make that kind of argument you have to cover all the options, and at least place those languages somewhere on the diagram (or acknowledge why you aren't).
> You can certainly go ahead and ask them "Why not use OCaml, F#, Haskell or Scala", or you can use FP to make a better project in this field to show people FP is indeed much better fit.
Well, that's what I do. But Go seems to get a disproportionate amount of coverage on HN, particularly compared with what people I've met in real life are using. (at least here in London. Maybe it's much more popular in SV?)
> Well, that's what I do. But Go seems to get a disproportionate amount of coverage on HN, particularly compared with what people I've met in real life are using. (at least here in London. Maybe it's much more popular in SV?)
There are quite a lot middle/small scale companies using Go in US. I can give you a long list if you want, but you can probably just search it by yourself.
I can not speak for Brad, but my impression on his comparison is that he picks up languages which are more used in the field Go is targeting at, not those which are only perfect fit in theory
He mentions Clojure, Haskell, and Erlang. Did you read the whole presentation?
I concur with his fun vs. efficiency graph. It strikes me as quite accurate. Also, I concur that it's a big win to not need nginx/Haproxy as a reverse proxy/SSL terminator. I suspect that a great deal of my server performance gain going from Clojure to Go resulted from this alone. Also concur that getting off the JVM has improved programmer fun by eliminating initial wait times.
Remind me again why you need HAProxy/Nginx ? SSL support in the JVM space is first class and there are plenty of libraries that negate any need for a separate infrastructure piece.
Also the argument about initial wait times is bizarre considering you are talking about server side applications. Plus there is always Drip.
Remind me again why you need HAProxy/Nginx ? SSL support in the JVM space is first class and there are plenty of libraries that negate any need for a separate infrastructure piece.
Clojure community at the time was still recommending using a reverse proxy as an SSL terminator. Are you going to say that the JVM SSL implementation is as fast as using nginx? It's also supposed to give some measure of DOS immunity.
Also the argument about initial wait times is bizarre considering you are talking about server side applications.
Yeah...Java guys were saying that back when I was working with app servers on Smalltalk VMs, which also had a tremendous advantage in startup times. (Take away the splash and herald, and VisualWorks used to start faster than Perl 5.) All of those several second wait times nickel and dime you to distraction.
Plus there is always Drip.
I did use it. About halved my wait times. The wait was still too long. Using Go is better for me.
> Clojure community at the time was still recommending using a reverse proxy as an SSL terminator. Are you going to say that the JVM SSL implementation is as fast as using nginx? It's also supposed to give some measure of DOS immunity.
The question isn't whether it's as fast as nginx so much as whether it's as fast as go.
There's performance, then there's programmer speed. I don't have to read disassembled bytecodes to be sure I'm getting 64 bit integer arithmetic. I don't have to do goofy things to write mutating code in performance critical loops.
Go is faster than Clojure plus nginx running my algorithms. What's more, the Go server actually got faster after turning on TLS. I suspect it retires bad connections more efficiently.
Agree. If only Go had generics, it would be so much more pleasant to use. I could deal with the absent of other constructs (exceptions, sum types, etc...). Yet, I'm anyway moving into Go (for back-end) for pragmatic reasons - availability of remote jobs and my preference of using it over j2ee or python/ruby/etc...
I don't mind Go. But I would hardly consider it "fun". The error handling is a chore, it's lack of libraries frustrating and you don't get the more interesting constructs of a FP language.
I wonder what you're doing that you find a lack of libraries. I have had no such problem, but I could imagine math or scientific libraries being in short supply... it's not really a language those fields have found suitable for their needs. Most stuff you'd use in a networked server application or command line tool exists.
The real difference IMHO is the very low cognitive load that Go requires. It's a very minimal language very easy to reason about. This translates into 2 things:
1. Most programers (above and below average) can just pick-up Go and be productive without much hand-holding.
2. Most programers can, without much experience, pick anyone's Go code and understand what it's doing.
Working in a company where we introduced Go for backend services without anyone knowing much about it, I know first hand those 2 are true for Go. But I'm not so sure about how OCaml, F#, Haskell or Scala would do with some of my colleagues in that sense.
That's an awesome thing about Go, there's nearly no ramp-up time. On Juju, we hire like 65% devs who've never used Go, spend nearly zero time teaching it to them, and they're still instantly productive.
So, you enjoy spending months ramping up new developers in your language of choice? That's not a task that falls to managers, it falls to the other developers on the team. There's still ramp up on the codebase, but at least I don't have to ramp people up on the basics of the language itself. That's a huge win.
If you think team productivity only matters to managers, that's kind of sad.
Then you're narrowing your hiring pool. Hiring is hard. Hiring really good people is really hard. Removing the requirement of "has 10 years of $LANGUAGE" is pretty awesome. It means we can hire someone because they're GOOD, not because their last job chose the same language we're using.
I'm willing to spend time ramping up if it means greater productivity in the long run. I can see that go's a kind of lowest-common-denominator language - you don't spend any time learning its fancy error-handling facilities because it doesn't have any - but I don't see that as a win.
You should use Go for a while. It's very productive. Lean and mean and gets out of the way of the developer.
People call it a lowest common demoninator language as a way to dismiss it, but it's actually incredibly well thought out by a lot of really smart people, several of whom have been writing code for longer than most people on HN have been alive.
The error handling works the way it does, because errors are not special. They're just values, and you can use the whole programming language to handle them just as you would any other value that gets returned.
> The error handling works the way it does, because errors are not special. They're just values, and you can use the whole programming language to handle them just as you would any other value that gets returned.
Sure, I actually like that. But I need a type system that's sophisticated enough to let me handle them nicely; there are times when I need eithers or validations or the like. More than that, there are enough cases where I need one or the other that I need to write code that can handle any kind of context generically.
It's funny because despite the reputation Scala (my language of choice) is actually very simple at the language level. But what you can do with those simple pieces can become very complex, and the range of patterns that are possible takes a while to learn. It's not learning the language that takes ramp-up time, it's the library code.
I predict that if Go is successful it will acquire its own scalaz-equivalent; it pretty much has to, because there are patterns in the way you do things like error handling that it would be stupid to just repeat in every project. I mean, you know what else was a small simple language? Java, which is why it acquired the massive frameworks it's best known for.
(That's not the worst possibility though; that would at least make Go a useful language. But with a type system noticeably worse than Java's, I'm not sure it will be possible to build these things in Go, and a type system isn't something you can bolt-on in a library.)
For fun, many years ago, I did a series of benchmarks of ack vs grin (both are programmers greps, one in Perl the other in Python, and both were written and maintained by excellent developers well-known in their respective languages). Because the tools do almost identical work, and can be configured to behave nearly identically, in terms of enabled features, types of searching to do, etc. I believe it's a useful real world performance test.
At the time, ack (in Perl) ran in roughly a tenth the time of grin (in Python) on most tests. Similarly, find+grep with the right options (and occasionally run through sed or awk to get further refined results) was approximately an order of magnitude faster than ack (but was much harder to use, and so ack or grin were still preferable tools).
It would likely be useful to repeat the experiment with modern Perl and Python versions. Both have improved and likely have better performance. It seems likely Python will have closed the performance gap some in the ensuing years.
It's also worth keeping in mind that this may be a sweet spot for Perl, given its genesis as a language processing language. And, it may be a tough one for Python given its historic aversion to regular expressions as a common solution to problems. So, saying Perl is (or was) ten times faster for this class of problem may simply mean this is a problem that Perl is particularly good at, and may not cover other classes of problem.
I'm suspicious of microbenchmarks, though enough microbenchmarks can give some sort of indication of what larger programs will do. But, they may also be indicative of the quality of the programmer contributing solutions...a better algorithm implementation can often erase language differences. Which is why I liked comparing two nearly identical programs, both by known excellent developers and not doing anything particularly prone to algorithmic complexity, as a means of testing language performance.
I imagine one could find other examples that would be good to compare. scons and cons, perhaps, would be another decent choice, assuming cons is still actively maintained.
What Go really lacks right now for oldtimers like me: NOT generics, but a good single-step debugger. We've had these almost 30 years ago built into our awesome Assembler (Devpac ST anyone?), C, Pascal etc. IDEs. Go is still lacking even proper gdb support and IDEs build on this shaky foundation. Why?
Because the people who need it most don't actually need it that much, otherwise they would have written it. When I debug most Go programs, I'm doing high-level debugging. A debugger is a sharp tool. For 90% of my debugging task I don't need scalpels, I need tools that can provide a holistic view of a system from above. I use DTrace for that.
When I actually use a debugger, it's low-level work; I either debug the runtime or the compiler and I use assembly. Mdb/gdb/acid is perfectly adequate for that. A debugger with special Go support is of no use to me.
That being said, a Go debugger is on the table.
Yes, on the Internet everyone wants everything, generics, IDE, higher order metaprogramming, etc. In the end, people who actually implement any tool, feature, or process are doing it only because it helps their concrete work in a very practical way. Yes, a debugger would be useful, but almost anything is useful in isolation. So far, the people who do the work didn't write a debugger, because even if it might have been useful to some, it would have been a net loss for their particular problem they were solving at that particular time, because of the additional effort.
I'm writing the arm64 compiler. I wrote a CPU simulator and a debugger. It's a very low-level tool and very useful to me at this particular stage in the port. Even though it took time to write it, it saves time overall. It's useless for 99.99% of other Go programmers.
No sorry, the work is relatively too early for that yet. Significant progress has been made, but a lot is still to be done. The Plan 9 C compiler included with Go works well but the Go compiler not yet. I'm pushing for inclusion in 1.4.
Forgive me for asking (not usually a programmer of anything that goes directly to machine code), but is single step debugging as relevant for a language such as Go which has far fewer ambiguities and undefined behaviour? Isn't single step debugging used precisely because C/C++ code generation is less predictable than Go's?
No. It's really useful to be able to just look at what's happening; it lets you do binary search to figure out where your assumptions are wrong, rather than having to verify every individual step. I use Scala which can provide much stronger static guarantees than Go, but a debugger is still very valuable.
I don't really understand crypto, but I suspect while adding memory safety high level primitives without any guarantees about their low-level behavior could lead to various side-channel attacks.
When implementing AES some had to resort to SIMD assembly instructions[1] instead of plain C to avoid timing attacks.
Yes, and the Go implementation takes all this into consideration (whether it does a good enough job is not decided yet).
The timing behaviour of the compiled Go code is very well understood, and it is (or can be made) very similar to the behaviour for C. When this is not enough, assembly is used.
It certainly does, but is it really all of this? For example, I had read that AES has a timing attack[1] due to non-constant time array access due to CPU caches being significantly faster that RAM. I don't see `ConstantTimeArrayAccess` there.
And when you have to fall back to assembly (like Go does for x86_64[2]), grandparent posts' argument for high-level languages as a less error prone is getting weaker (although it still stands true, as attack surface is shallower)
Several classes of security problem, including the Heartbleed bug, are impossible or very unlikely in a language that doesn't have manual memory management.
His assertion is theoretical...he assumes a Go implementation will be superior based on this removal of a huge class of potential problems. But, it is difficult to be certain without an audit and a lot of real world testing and experience with the library. It is not unreasonable, however, to assume that security libraries in a language like Go could be safer than similar libraries in a language with manual memory management like C/C++.
Purely functional programming and programming without side effects (as in Haskell) can also reduce some types of error and make for more provably correct software, but Go is not particularly pure in this regard...but, as I understand it, can be used in a manner that is similar in many ways. Again, this could prevent entire classes of common security problem.
Thanks for nice explanation. My crypto class teacher on coursera (Dan Boneh of Stanford) used to repeat one thing again and again; "Don't design or implement crypto systems yourself". His argument was that crypto required the validation of time and a lot of experts (working as adversaries). From this point of view, a young language or implementation seems to be at a disadvantage compared to older ones. Of course fresh memories of Hearbleed seems to contradict what I say. But what doesn't kill OpenSSL makes it stronger or (get forked).
it is kind of interesting to see godrone.io being mentioned here, where the f/w for the parrot drone is in go. for comparison with erlang, here it is being demoed with in-flight hot-code-reload :) http://www.erlang-embedded.com/2013/10/minimal-downtime-in-f...
I've created a couple of projects in Go so far (a map server, similar to TileStache etc.) - Sure, the lack of generics is annoying, but I think the real elephant in the room is dependency and package management. The way Go expects you to organize your source code (the workspace concept, GOPATH, go get and github-repos as import paths) is a huge road block here. To this day the Go community hasn't settled on any kind of solution. If you bring any of this up on the mailing list you'll be crucified by the Go zealots who think Go's way of doing things is a thing of immaculate perfection.
If you want to be safe you will have to version your dependencies with your project. So one way to achieve this is to put your entire GOPATH src folder into your project repository. Of course this defeats the purpose of resolving "github.com/myname/myproject" with "go get". Or, more annoying still, you'll put them into a /vendor/ folder inside your package folder and rewrite all the import paths. So "github.com/myname/myproject/vendor/somepackage".
Of course, if you simply rely on "go get" then dependency hell is just around the corner when it comes to transient dependencies. A -> C-v1 and A -> B -> C-v1.1. In fact if the entire Go eco-system relied on "go get" there would be no guarantee that a library worked - upstream dependencies could change at any moment.
The way Go expects you to organize your source code (the workspace concept, GOPATH, go get and github-repos as import paths) is a huge road block here. To this day the Go community hasn't settled on any kind of solution.
This has been a major roadblock for me. I've worked with a whole bunch of different languages, and this drives me completely nuts.
I actually love it. Here's what you do: don't use "workspaces". Keep all your code in one GOPATH, preferably $GOPATH=$HOME. That's it. 99% of the time that'll work great.
It is an attempt to make things still go gettable and maintain versioning so that API breaking changes don't ruin your go get. I think it's a good start.
I have used Go 2-3 years fulltime and I didn't find it a problem. Right now I'm using node.js and I wonder why there are more than a dozen packages with that exist in multiple versions in my npm. This is absolutely confusing.
Today I'm working on a project A which depends on B, which depends on C, which depends on D. In order to fix a bug in D, it seems that I need to make a personal copy of B and C in order to rewrite their import statements, and then I'm responsible for keeping all those packages up to date :/
(Unless I'm missing something, I am pretty new to Go. I really hope I am missing something...)
So, assuming that upstream will accept your changes, the easiest thing to do is create a branch of D in the same on-disk location as the original code (so, $GOPATH/src/github.com/D-team/D for example). You then make your modifications there... the import statements still work because they still refer to the same place on disk.
> Of course, if you simply rely on "go get" then dependency hell is just around the corner when it comes to transient dependencies. A -> C-v1 and A -> B -> C-v1.1. In fact if the entire Go eco-system relied on "go get" there would be no guarantee that a library worked - upstream dependencies could change at any moment.
And that's a reason, why go projects should (at least) tag or branch properly. Other projects in other languages do releases, so that
you can rest assured that your project works properly with version X of a third party dependency.
If a go dependency properly tags or versions its code upstream, you can use that specific tag, which should give you some safety (at
least as much as with the traditional release package mechanism in other languages).
I do not see a big deal here within the go approach itself. The problem is that many just do not do this properly yet but instead
push every change to their main branch, without thinking about the possible impact for consumers of their go packages.
A -> C-v1 and A -> B -> C-v1.1 is a problem in any language.
As for the rest, you can have your cake and eat it too. Go get is useful for getting trunk, so let it get trunk. You can use Keith Rarick's godeps to pin branch revisions on release branches to have repeatable builds there. Then go get can still get trunk, which is what most devs will want to do to start hacking on it (and honestly, no one but devs are going to use go get, right?)
Yes, this means that if one of the repos you depend on goes offline, you have a problem.... but this is DVCS, you almost certainly have N backups of that repo, where N equals the number of developers on your team. So it's not like you're hosed for forever.
If you want to be really paranoid you can use godeps or third_party, or one of the many other vendoring tools to keep all the code in your repo. This is a lot like keeping libraries in your repo in other languages.
It's not really a problem, in fact, you have many different ways to solve dependency management in Go. That's not a bad thing. You just need to choose one, (or not, and choose your upstreams wisely).
> A -> C-v1 and A -> B -> C-v1.1 is a problem in any language.
That is inaccurate in two ways. First, it isn't a problem in node.js. Second, it isn't a language problem, it is an algorithm problem. dpkg has the same issue, but nix is immune to it.
I'd also like to point out how bad those things were in C (and C-style languages). Go is definitely an improvement, but it isn't the end of the road.
How is this not a problem in node.js? Keep in mind, I don't know node.
However, in general, there's going to be several common problems in any language:
Values from v1 and v1.1 won't interoperate perfectly. If you get a v1 object and pass it to B which uses v1.1 to operate on it, stuff may blow up / not work correctly, which may only be obvious in rare runtime situations.
Any external resources used by both v1 and v1.1 will now be in contention. If both of them try to open port 12345, or write to the same config file, for example.
And that assumes that there's no ambiguity based on just importing almost the same code twice, like if you say C.Foo... which C? Can be gotten around by always namespacing the code via the version number, I suppose.
There's no magic fix for these problems. Some human has to go through all the dependencies and make sure that there won't be conflicts. Maybe v1 and v1.1 can coincide just fine, maybe they'll instantly explode. Computers can't figure that stuff out.
> How is this not a problem in node.js? Keep in mind, I don't know node.
The automated tool npm relies on a local nested tree. The A -> C-v1 and A -> B -> C-v1.1 problem would look like this:
(your code)
node_modules/
A/
(code for A)
node_modules/
C/
(code for C at v1)
B/
(code for B)
node_modules/
C/
(code for C at v1.1)
The whole tree is created painlessly when you enter `npm install A`. The program figures out the dependencies through JSON files bundled with each module. They include version requirements; npm simply chooses the highest version that respects those requirements. Within each directory, `require('C')` looks for `./node_modules/C/` and finds the module it actually needs. All versions of all modules are kept at all times on a central public server at http://npmjs.org/.
This design is only possible in some languages. Dart, for instance, cannot apply that to its libraries because its types are globally defined and would therefore clash.
In Go, types are not global, which means this design can apply in principle, according to the Go Spec. However, the implemented import syntax does not act in a way that would make this possible. Oddly enough, that syntax is not specified in the Go Spec:
> The interpretation of the ImportPath is implementation-dependent but it is typically a substring of the full file name of the compiled package and may be relative to a repository of installed packages.
Getting the modules and making the tree is easy. Making the code work is hard.
Yes, sometimes this will work... but if A passes an object from v1 into B and B passes that object into v1.1... that can blow up. If v1 and v1.1 both use any global resources, that can blow up.
It doesn't sound like node addresses those problems at all, which are the only ones that are actually difficult.
> A -> C-v1 and A -> B -> C-v1.1 is a problem in any language.
A language that mangles exported symbols with version information can achieve this (with the exception of two versions of a lib trying to contest the same global resource). "A" gets the version of "C" that it expects and "B" gets the version of "C" that it expects, with no name resolution conflicts or method lookup ambiguities.
This does require support from the language, though. You need a standard means of mangling symbols, a way to specify version information for each package, a way to qualify package imports with version info, and so on.
Yeah, it's the global resources that are the biggest problem, though. Certainly, if you can namespace everything, then the code itself won't fall over. If you version the URL in Go, you can do that too. Stuff that uses v1 can live along side the stuff that uses 1.1. THey won't be compatible in that you can't pass a v1.Foo into something that wants a v1.1.Foo... but that's not a good idea anyway.
http://labix.org/gopkg.in is a rather good solution for distributing libraries with transitive dependencies. It requires critical mass for it to really take off, but the level of effort is extremely low for implementation (make a branch on github, don't break it... done!). I think this low level of effort is really important, as I have been able to simply poke authors of my favorite packages for support, and had it in a day or so.
As for an internal project (non-distributed) I think vendoring is the solution everyone who is sane has always been using, why would you risk you project on a random guy deciding he wants to delete his github account.
Go's lack of generics kills it for me, as I've learned trying to implement a persistent collection type recently. Without any concept of parameterised types (Foo<A>), programmer defined container types are second class citizens. Casting things to (interface{}) and back everywhere gets old quickly, and you get no static type safety. So in that domain it's essentially back to the C way of doing things, with (void*) everywhere.
In slide #28 it is claimed that Go is as fast as Java. I don't use Java extensively but I always had the impression that was quite performant with the highly tuned HotSpot and all even more so than Go. Are there any comparative benchmarks between Go and Java for similar-ish tasks? I get it that Go probably handles concurrency in a more elegant way but I have some doubts regarding raw performance. I am talking about the standard compiler implementation, not gccgo.
Oh yeah, Java's garbage collector is a lot better than Go's, though Go's is improving. That being said, it's way easier to simply not create garbage in Go than it is in Java, so you can avoid the garbage collector entirely.
87 comments
[ 3.5 ms ] story [ 141 ms ] threadI thought the benefits of goroutines were now about more efficient scheduling, not lower memory utilization?
A particular point of goroutines is that they use less address space than threads. Both goroutines and threads use roughly the same amount of physical memory.
Sure, functional language can be used in almost all the programming related projects. But are they really a good fit or not is not decided by FP advocacy, but the people who actually tackle these problems.
Go for now shines in a pretty niche market, it is extremely good fit for this niche market, and this fact is not advocated by Go fans or Go team, but by large amount of open source projects and emerging usage in quite a few serious companies/startups.
You can certainly go ahead and ask them "Why not use OCaml, F#, Haskell or Scala", or you can use FP to make a better project in this field to show people FP is indeed much better fit.
To some of us, programming language is merely a tool to solve a problem, not to show off the shining design concept. For us, Go hits a incredibly sweet point on the balance of coding efficiency and running efficiency. Note that, coding efficiency has nothing to do with "how beautiful and elegant this code snippet is", it has everything to do with "if this project is easy to read, easy to maintain and easy to expand"
Those languages are better tools for solving problems in a lot of cases, the problem is people just choose tools that are similar to ones they already know rather than the best tools available.
Go to github and see the rank of project written in Go, you will see clearly what is the niche market(not that niche I would say) I'm talking about.
The point stcredzero about manually laying out memory is fair, though much less important in writing web servers than in a game.
A lot of the slides are about why he wouldn't use Perl, why he wouldn't use Java, and the like. His whole argument is that there's no other language in the efficient / nice to program quadrant. To make that kind of argument you have to cover all the options, and at least place those languages somewhere on the diagram (or acknowledge why you aren't).
> You can certainly go ahead and ask them "Why not use OCaml, F#, Haskell or Scala", or you can use FP to make a better project in this field to show people FP is indeed much better fit.
Well, that's what I do. But Go seems to get a disproportionate amount of coverage on HN, particularly compared with what people I've met in real life are using. (at least here in London. Maybe it's much more popular in SV?)
There are quite a lot middle/small scale companies using Go in US. I can give you a long list if you want, but you can probably just search it by yourself.
I can not speak for Brad, but my impression on his comparison is that he picks up languages which are more used in the field Go is targeting at, not those which are only perfect fit in theory
Go(ne ?)
I concur with his fun vs. efficiency graph. It strikes me as quite accurate. Also, I concur that it's a big win to not need nginx/Haproxy as a reverse proxy/SSL terminator. I suspect that a great deal of my server performance gain going from Clojure to Go resulted from this alone. Also concur that getting off the JVM has improved programmer fun by eliminating initial wait times.
Also the argument about initial wait times is bizarre considering you are talking about server side applications. Plus there is always Drip.
Clojure community at the time was still recommending using a reverse proxy as an SSL terminator. Are you going to say that the JVM SSL implementation is as fast as using nginx? It's also supposed to give some measure of DOS immunity.
Also the argument about initial wait times is bizarre considering you are talking about server side applications.
Yeah...Java guys were saying that back when I was working with app servers on Smalltalk VMs, which also had a tremendous advantage in startup times. (Take away the splash and herald, and VisualWorks used to start faster than Perl 5.) All of those several second wait times nickel and dime you to distraction.
Plus there is always Drip.
I did use it. About halved my wait times. The wait was still too long. Using Go is better for me.
The question isn't whether it's as fast as nginx so much as whether it's as fast as go.
Go is faster than Clojure plus nginx running my algorithms. What's more, the Go server actually got faster after turning on TLS. I suspect it retires bad connections more efficiently.
I don't mind Go. But I would hardly consider it "fun". The error handling is a chore, it's lack of libraries frustrating and you don't get the more interesting constructs of a FP language.
1. Most programers (above and below average) can just pick-up Go and be productive without much hand-holding.
2. Most programers can, without much experience, pick anyone's Go code and understand what it's doing.
Working in a company where we introduced Go for backend services without anyone knowing much about it, I know first hand those 2 are true for Go. But I'm not so sure about how OCaml, F#, Haskell or Scala would do with some of my colleagues in that sense.
If you think team productivity only matters to managers, that's kind of sad.
People call it a lowest common demoninator language as a way to dismiss it, but it's actually incredibly well thought out by a lot of really smart people, several of whom have been writing code for longer than most people on HN have been alive.
The error handling works the way it does, because errors are not special. They're just values, and you can use the whole programming language to handle them just as you would any other value that gets returned.
Sure, I actually like that. But I need a type system that's sophisticated enough to let me handle them nicely; there are times when I need eithers or validations or the like. More than that, there are enough cases where I need one or the other that I need to write code that can handle any kind of context generically.
It's funny because despite the reputation Scala (my language of choice) is actually very simple at the language level. But what you can do with those simple pieces can become very complex, and the range of patterns that are possible takes a while to learn. It's not learning the language that takes ramp-up time, it's the library code.
I predict that if Go is successful it will acquire its own scalaz-equivalent; it pretty much has to, because there are patterns in the way you do things like error handling that it would be stupid to just repeat in every project. I mean, you know what else was a small simple language? Java, which is why it acquired the massive frameworks it's best known for.
(That's not the worst possibility though; that would at least make Go a useful language. But with a type system noticeably worse than Java's, I'm not sure it will be possible to build these things in Go, and a type system isn't something you can bolt-on in a library.)
If you're just an average programmer you can probably still go program enterprise software somewhere big where personal accountability is lacking.
If it wasn't for the NDAs I would have lots of war stories to tell.
If it wasn't for Google sponsoring, it would have joined Limbo already.
But basically they seem to be both in the same category.
At the time, ack (in Perl) ran in roughly a tenth the time of grin (in Python) on most tests. Similarly, find+grep with the right options (and occasionally run through sed or awk to get further refined results) was approximately an order of magnitude faster than ack (but was much harder to use, and so ack or grin were still preferable tools).
It would likely be useful to repeat the experiment with modern Perl and Python versions. Both have improved and likely have better performance. It seems likely Python will have closed the performance gap some in the ensuing years.
It's also worth keeping in mind that this may be a sweet spot for Perl, given its genesis as a language processing language. And, it may be a tough one for Python given its historic aversion to regular expressions as a common solution to problems. So, saying Perl is (or was) ten times faster for this class of problem may simply mean this is a problem that Perl is particularly good at, and may not cover other classes of problem.
I'm suspicious of microbenchmarks, though enough microbenchmarks can give some sort of indication of what larger programs will do. But, they may also be indicative of the quality of the programmer contributing solutions...a better algorithm implementation can often erase language differences. Which is why I liked comparing two nearly identical programs, both by known excellent developers and not doing anything particularly prone to algorithmic complexity, as a means of testing language performance.
I imagine one could find other examples that would be good to compare. scons and cons, perhaps, would be another decent choice, assuming cons is still actively maintained.
When I actually use a debugger, it's low-level work; I either debug the runtime or the compiler and I use assembly. Mdb/gdb/acid is perfectly adequate for that. A debugger with special Go support is of no use to me.
That being said, a Go debugger is on the table.
Yes, on the Internet everyone wants everything, generics, IDE, higher order metaprogramming, etc. In the end, people who actually implement any tool, feature, or process are doing it only because it helps their concrete work in a very practical way. Yes, a debugger would be useful, but almost anything is useful in isolation. So far, the people who do the work didn't write a debugger, because even if it might have been useful to some, it would have been a net loss for their particular problem they were solving at that particular time, because of the additional effort.
I'm writing the arm64 compiler. I wrote a CPU simulator and a debugger. It's a very low-level tool and very useful to me at this particular stage in the port. Even though it took time to write it, it saves time overall. It's useless for 99.99% of other Go programmers.
Just curious, why Go AES is safer than OpenSSL or GnuTLS, anyone? Or safer here means something other than secure?
That being said, the AES implementation uses fast assembly code, if possible, so some safety is lost.
In any case, TLS would have provided a better example, AES is much easier to audit for correctness than TLS.
When implementing AES some had to resort to SIMD assembly instructions[1] instead of plain C to avoid timing attacks.
[1]: http://crypto.stanford.edu/vpaes/
The timing behaviour of the compiled Go code is very well understood, and it is (or can be made) very similar to the behaviour for C. When this is not enough, assembly is used.
Please see http://golang.org/pkg/crypto/subtle.
It certainly does, but is it really all of this? For example, I had read that AES has a timing attack[1] due to non-constant time array access due to CPU caches being significantly faster that RAM. I don't see `ConstantTimeArrayAccess` there.
And when you have to fall back to assembly (like Go does for x86_64[2]), grandparent posts' argument for high-level languages as a less error prone is getting weaker (although it still stands true, as attack surface is shallower)
[1]: http://cr.yp.to/antiforgery/cachetiming-20050414.pdf
[2]: http://golang.org/src/pkg/crypto/aes/asm_amd64.s
His assertion is theoretical...he assumes a Go implementation will be superior based on this removal of a huge class of potential problems. But, it is difficult to be certain without an audit and a lot of real world testing and experience with the library. It is not unreasonable, however, to assume that security libraries in a language like Go could be safer than similar libraries in a language with manual memory management like C/C++.
Purely functional programming and programming without side effects (as in Haskell) can also reduce some types of error and make for more provably correct software, but Go is not particularly pure in this regard...but, as I understand it, can be used in a manner that is similar in many ways. Again, this could prevent entire classes of common security problem.
If you want to be safe you will have to version your dependencies with your project. So one way to achieve this is to put your entire GOPATH src folder into your project repository. Of course this defeats the purpose of resolving "github.com/myname/myproject" with "go get". Or, more annoying still, you'll put them into a /vendor/ folder inside your package folder and rewrite all the import paths. So "github.com/myname/myproject/vendor/somepackage".
Of course, if you simply rely on "go get" then dependency hell is just around the corner when it comes to transient dependencies. A -> C-v1 and A -> B -> C-v1.1. In fact if the entire Go eco-system relied on "go get" there would be no guarantee that a library worked - upstream dependencies could change at any moment.
This has been a major roadblock for me. I've worked with a whole bunch of different languages, and this drives me completely nuts.
It is an attempt to make things still go gettable and maintain versioning so that API breaking changes don't ruin your go get. I think it's a good start.
(Unless I'm missing something, I am pretty new to Go. I really hope I am missing something...)
Read this: http://blog.natefinch.com/2014/03/go-and-github.html
And that's a reason, why go projects should (at least) tag or branch properly. Other projects in other languages do releases, so that you can rest assured that your project works properly with version X of a third party dependency. If a go dependency properly tags or versions its code upstream, you can use that specific tag, which should give you some safety (at least as much as with the traditional release package mechanism in other languages).
I do not see a big deal here within the go approach itself. The problem is that many just do not do this properly yet but instead push every change to their main branch, without thinking about the possible impact for consumers of their go packages.
As for the rest, you can have your cake and eat it too. Go get is useful for getting trunk, so let it get trunk. You can use Keith Rarick's godeps to pin branch revisions on release branches to have repeatable builds there. Then go get can still get trunk, which is what most devs will want to do to start hacking on it (and honestly, no one but devs are going to use go get, right?)
Yes, this means that if one of the repos you depend on goes offline, you have a problem.... but this is DVCS, you almost certainly have N backups of that repo, where N equals the number of developers on your team. So it's not like you're hosed for forever.
If you want to be really paranoid you can use godeps or third_party, or one of the many other vendoring tools to keep all the code in your repo. This is a lot like keeping libraries in your repo in other languages.
It's not really a problem, in fact, you have many different ways to solve dependency management in Go. That's not a bad thing. You just need to choose one, (or not, and choose your upstreams wisely).
That is inaccurate in two ways. First, it isn't a problem in node.js. Second, it isn't a language problem, it is an algorithm problem. dpkg has the same issue, but nix is immune to it.
I'd also like to point out how bad those things were in C (and C-style languages). Go is definitely an improvement, but it isn't the end of the road.
However, in general, there's going to be several common problems in any language:
Values from v1 and v1.1 won't interoperate perfectly. If you get a v1 object and pass it to B which uses v1.1 to operate on it, stuff may blow up / not work correctly, which may only be obvious in rare runtime situations.
Any external resources used by both v1 and v1.1 will now be in contention. If both of them try to open port 12345, or write to the same config file, for example.
And that assumes that there's no ambiguity based on just importing almost the same code twice, like if you say C.Foo... which C? Can be gotten around by always namespacing the code via the version number, I suppose.
There's no magic fix for these problems. Some human has to go through all the dependencies and make sure that there won't be conflicts. Maybe v1 and v1.1 can coincide just fine, maybe they'll instantly explode. Computers can't figure that stuff out.
The automated tool npm relies on a local nested tree. The A -> C-v1 and A -> B -> C-v1.1 problem would look like this:
The whole tree is created painlessly when you enter `npm install A`. The program figures out the dependencies through JSON files bundled with each module. They include version requirements; npm simply chooses the highest version that respects those requirements. Within each directory, `require('C')` looks for `./node_modules/C/` and finds the module it actually needs. All versions of all modules are kept at all times on a central public server at http://npmjs.org/.This design is only possible in some languages. Dart, for instance, cannot apply that to its libraries because its types are globally defined and would therefore clash.
In Go, types are not global, which means this design can apply in principle, according to the Go Spec. However, the implemented import syntax does not act in a way that would make this possible. Oddly enough, that syntax is not specified in the Go Spec:
> The interpretation of the ImportPath is implementation-dependent but it is typically a substring of the full file name of the compiled package and may be relative to a repository of installed packages.
Yes, sometimes this will work... but if A passes an object from v1 into B and B passes that object into v1.1... that can blow up. If v1 and v1.1 both use any global resources, that can blow up.
It doesn't sound like node addresses those problems at all, which are the only ones that are actually difficult.
This does require support from the language, though. You need a standard means of mangling symbols, a way to specify version information for each package, a way to qualify package imports with version info, and so on.
As for an internal project (non-distributed) I think vendoring is the solution everyone who is sane has always been using, why would you risk you project on a random guy deciding he wants to delete his github account.
Go's lack of generics kills it for me, as I've learned trying to implement a persistent collection type recently. Without any concept of parameterised types (Foo<A>), programmer defined container types are second class citizens. Casting things to (interface{}) and back everywhere gets old quickly, and you get no static type safety. So in that domain it's essentially back to the C way of doing things, with (void*) everywhere.
Go is pretty close to java's speed. Generally slower, but not by a lot.