Go: 90% perfect, 100% of the time
talks.golang.org
talks.golang.org
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:
(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.
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.
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.
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.
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.
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.
Go is pretty close to java's speed. Generally slower, but not by a lot.
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.
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)
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.
I 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.
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.