Six years of Go
blog.golang.org
blog.golang.org
On one hand, it addresses many of the pain points I've experienced with other languages. It's easy to build and deploy, reasonably performant, and has a powerful and consistent standard library.
On the other… developing in it feels like a total slog. It manages to be simultaneously far too anal and overly forgiving about syntax. Visibility definition using upper/lowercase is a shite idea. Package names (and packaging generally) are a mess. Magical built-in globals are a huge design smell. The lack of generics, as cliché a complaint as it is, results in loads of duplicated or generated code when building anything more complex than the simplest app. And so on.
I find the whole experience of using it to be unpleasant (rather like using a blunt knife), and yet it fits a set of requirements that mean I keep developing new projects using it – particularly high-performance small services that do things like fling JSON around, for which the built-in HTTP libraries + Gin are brilliant.
I'm looking forward to the point that a (trans|com)piler which smooths over the bad parts is available.
A diagnostic indicator is whether you gravitate towards things like Martini or Gin, which aren't idiomatic Go and seem designed to ease the pain of using Go for those who don't actually care for it much.
I love martini (and lately gin) and definitely love and use go every day. What about these frameworks is not idiomatic go?
Is there another framework that is? Or you suggest just rolling your own using net/http by itself?
Cheers
tl;dr - Martini doesn't have a strict, type-safe API. Overuse of reflection leads to a small performance loss but a much larger, much more important cognitive tax.
"Slow" in the case of a router/mux is entirely relative though. There's been this strange focus on routing speed/allocations, when its the smallest slice of your total request-to-response performance. Martini's router won't make your application noticeably slower than any other.
I have enjoyed the gin API. In maybe 10 lines of code, I can build a fast HTTP service that can serve up a small single-page app. The same thing using e.g. Negroni feels like I'm writing more boilerplate for no apparent gain – and I'm a great opponent of writing boilerplate code.
A good example is errors being values — which is a great idea. But then you realize every single function needs to be littered 1-10 cases of
if err != nil {
return nil, err
}
It's an extremely common pattern. It's tiring to write, over and over. Tiring to refactor, too: If you change a signature (to add an error, or add another return value, for example), every error check has to be updated.Unfortunately, Go doesn't offer any abstractions that might allow you to avoid such boilerplate. Functions that return errors cannot ever be chained, for example: If you have "func bar() (MyStruct, error)", you cannot do "foo(bar())". You must always assign the result to an intermediate variable. You could embed the error in the MyStruct struct instead, but that goes against Go's grain.
I find myself wishing for some magic macro that is smart enough to extract a value and return the error for me. Something like:
try! value, err := bar()
...would expand to: value, err := bar()
if err != nil {
return
}
I'm with you on upper/lower-case names. It results in schizophrenic-looking programs. Unless I'm writing a library, I tend to just export anything that isn't obviously an internal implementation detail, for consistency.I'm also up in arms about ":=" assignment behaviour. I've run into several bugs caused by shadowing inside blocks. You have to be careful when refactoring so as not to cause shadowing issues; really, editors should have their syntax highlighting set to highlight ":=" in blinking bright yellow or something. It's so hard to miss.
It's also inconsistent with how you (or, at least, I) want it to behave. It will shadow existing variables by default if there is at least one new variable on the left-hand side, but that's the least conservative behaviour, and feels contrary to Go's strictness — a language, after all, that considers unused imports to be a compilation error. Recently I've started avoiding ":=" in favour of vars, just to avoid falling into that trap by accident.
To be fair, I love many aspects of Go: Compilation speed, relative performance, ease of concurrency, static strictness. But after working with Go for a while and being quite productive with it, I'm at the same time seriously pining for a better language to replace it.
value, err := bar(); err {
// there was an error
}
I like how that looks, personally. Obviously YMMV. if value, err := bar(); err != nil {
return err
}
Note that this syntax declares "value" in the "if" scope. You can only access it inside the "then" block or in an "else" block: if value, err := bar(); err != nil {
return err
} else {
log.Print(value) // this compiles
}
log.Print(value) // does not compile
So if you need it later, you have to do: var value MyType
var err error
if value, err = bar(); err != nil {
return err
}
Note that you cannot do this: var value MyType
if value, err := bar(); err != nil {
return err
}
This would, again, declare a new "value" that shadows the outer "value" inside the "if" scope. (Go would fail to compile it since it doesn't allow unused variables.)Chaining does work, but obviously the function needs the same signature as the return values, which limits things since most APIs don't accept errors as input. But within your code you can structure it to get error values for things you know you will be chaining. Or create wrappers to make your types chainable.
Still, having to let every function take an error parameter isn't really an option, generally.
It can't always be done. A generic wrapper only works for functions that return values. It would be nice to have a wrapper that can take any function, e.g. foo in:
package main
import "fmt"
func foo(a ...interface{})interface{}{
if len(a) == 0 {
return nil
} else if len(a) == 1 {
return a[0]
} else {
return a
}
}
func barbar() (a, b int) { return 1, 2 }
func bar() (a int) { return 1 }
func baz() { return }
func main() {
fmt.Println( foo(barbar()) ) // prints: [1, 2]
fmt.Println( foo(bar()) ) // prints: 1
fmt.Println( foo(baz()) ) // compile error: baz() used as value
}
Instead of the compile error, it would be nice if foo accepted baz() as a argument, which would read the array length as 0 and here return nil.I'm usually stuck using:
reply, err := redis.Givemethevalue(key)
if err != nil {
return err
}
thing, err2 := postgres.getById(reply)
if err2 != nil {
return err2
} reply, err := redis.Givemethevalue(key)
if err != nil {
return err
}
thing, err := postgres.getById(reply)
if err != nil {
return err
}
Go allows you to assign with := if at least one of the left-hand-side variables is new. var err error
at the beginning of the function if needed.BTW, if you really want to make sure you don't make mistakes, use the compiler tools, or, better yet, https://github.com/alecthomas/gometalinter .
struct fooer {
x int
blah string
// ... more state
err error
}
func Foo(x int) (string, error) {
f := &fooer{x: x};
defer f.cleanup()
f.stepOne()
f.stepTwo()
f.stepThree()
return f.blah, f.err
}
func (f *fooer) stepOne() {
// ... do stuff and maybe set f.err
}
func (f *fooer) stepTwo() {
if f.err != nil {
return
}
// ... do stuff and maybe set f.err
}
func (f *fooer) stepThree() {
if f.err != nil {
return
}
// ... do stuff and maybe set f.err; set f.blah
}
func (f *fooer) cleanup() {
// ...
}
While this is initially a tad more verbose, it has some nice benefits. For one thing, you can easily fmt.Printf("%#v", f) to dump all the relevant state. However, it also tends to be more resilient to refactoring:- No method signatures to patch up.
- Easier to non-local jump via return if you have some unusual control flow, etc.
- You can easily find all references to `f.err` to verify your error handling.
- The method names and step list take on a self-documenting quality.
Another key insight I had when learning to tolerate Go: It's ok to do throw away work. You don't need to abort instantly on failure, you can just make sure code paths handle zero values or other invalid states more robustly. For example, I sometimes add a function like:
func (f *fooer) fail(err error) {
if f.err == nil {
f.err = err
}
}
And then just add sprinkle some f.fail() calls, make code handle nils, invalid states, etc, and make cleanup code idempotent. Seems to work out much more nicely than if err, return.func checkErrPanic(err error) { if err != nil { panic(err) } }
func checkErrNonFatal(err error) { if err != nil { fmt.Println(err) return } }
In Pascal you would use := every time you wanted to assign a variable, and a single equals when testing, such as "if (x = 5) then...". It is slightly more like math than most languages, which I appreciated. But, golang only allows it on the first assignment, the reasoning perhaps that it is a declaration?
I don't feel that this third way of doing it with no advantage was a benefit, choosing one C of Pascal rules would have been better and less to think about.
I've come to believe that slicker error handling is a bit of a false economy, at least compared to other "informal" languages like Java, Ruby, and Python (all bets are off if you want to compare Go to Haskell; I concede everything in advance there).
I feel like a lot of Java and (especially) Python/Ruby code is easier to read... until it matures enough to handle errors properly, at which point its just as crudded up. It's much easier to start sketching out code in Ruby and wrapping everything in blanket try/rescue blocks. But that code always breaks and always gets trickier before it's ready to ship.
Lots of people I've talked to have had the same experience coming off C, Ruby, and Python as I have: when the Go compiler stops spitting out errors, your program tends to work --- usually not perfectly, but still far better than a first-run Ruby or C program does!
Also, when people think about Go error handling compared to Ruby and Python, they rarely factor in the density of unit tests required for the Ruby and Python code.
Go basically tells us that errors are as really important — they should be in your face and handled by the caller. And then it goes ahead and makes it a second-class citizen that is really awfully laborious to work with. Why not make error-handling a first-class construct?
Most code needs to just pass the error on to the caller, for example. Go would benefit from an easy way to just "bounce" the error off to the caller. Something like my naive, hypothetical macro in my previous comment.
Some sum-type-based solution like Rust's Result enum would also have made the whole problem pretty much moot.
Not only that, it's very easy to just dump errors on the floor, which is why Golang needs tools like errcheck.
For an easy example of this in practice (os.Setenv returns an error): https://github.com/search?l=go&q=os.Setenv&type=Code&utf8=%E...
v1, err := doStuff()
if err != nil {
return
}
var v2 MyStruct
if value != nil {
v2, err = doMoreStuff(
some_param,
different_param,
one_more_params)
}
// ... use v1 and v2
err = doYetMoreStuff()
I didn't catch the orphan err because I absent-mindedly focused on the parameter list to doMoreStuff() and just didn't see it.But I find it frustrating that Go will, on the one hand, angrily refuse to compile a program where a variable is unused, but on the other it will happily accept a program where a value is never used. Isn't that potentially as bad? I'd rather have it complain and force me to assign to _ if I really wanted to discard it.
Good point but exceptions or explicit handling (even with sum-type like in Rust) are not the only ways. There is also the crash-and restart way, Go has some with panic, Rust, Erlang is probably king here, can do it with OS processes as well. That of course, has to be handled well by the runtime, so either no shared memory so corruption doesn't spread arbitrarily throughout the application or provable compile time checking (like Rust).
:/ Their json system reflects and allocates like a mad-man. It's probably one of the slowest parts of the std library.
go-codec [1] is very good, and can do things like interning string keys to reduce allocation overhead, but it's not faster than encoding/json when working on plain maps.
Java can be terribly verbose at times and the culture often makes it worse, but it often doesn't feel like a slog in practice because errors are reported as you type, with quick fixes. Editor plugins for Go will tell you some of the errors, some of the time.
The slog seems to be in the frequent feeling of 'why do I have to do this – surely the computer should be doing it for me?'. This is mostly around things like list comprehension, where it feels like using stone-age tools. It sucks, because the development story around e.g. goroutines and channels is so elegant in comparison.
We've got a limit-order-book market with a FIX API and an HTTPS/JSON API, a bunch of market makers, an AVR emulator, a dispatcher to handle thousands of AVRs concurrently, a C compiler, HTTPS/JSON APIs to implement debug stubs for all of that, and a bunch of other random stuff --- and we have one (1) custom data structure --- the red-black tree we use for the order books themselves.
I'm a recovering C++ programmer and do not doubt for a moment that if Go had generics, I'd have availed myself of them. I'm not sure my code would be better for it, though.
This all may be the ramblings of someone basing their opinions on "code I've written that has worked" and not on some higher standard of design patterns, so feel free to ignore if that's not productive :)
This ties into other thoughts I have rattling around about treating languages as specialty tools with well prescribed patterns, vs the trend to implement some subset of common lisp as the saying goes, but not well formulated enough to expound on them here. Broadly; I'm not sure which paradigm I prefer. (Having a big toolbox of specialty tools is nice, but if one tool can easily eat the functionality of another tool, I tend to side on considering that option.)
I could use a lock free ring buffer in dozens of places. I could use promise/futures in dozens of places. I could use anything but the big honking lock that is channels in dozens of places.
I could use proper variance rules in the generics Go does provide in dozens of places. I could use monadic style errors and options in dozens of places.
Now, I agree that I generally don't need to write my own generic code very often, but if the blessed generic writers on the go lang compiler team don't write these things for me, I don't have a fall back option in Go (other than code gen, which people are starting to fall back on). It just seems unsustainable to me.
I don't think Go is a great language, as I'm fond of saying, I just think it's a very useful tool.
But it being a good tool should not be a deterrent from it becoming a better tool.
[edit] Edited to sound lest strident.
I won't harp on about generics in Go, because I think it's far from the biggest issue – but the hardcoded magic generics show that such a feature is useful. I'd love a simple set implementation, for example – but I often find myself reduced to writing dumb, ugly, difficult-to-read code to work around the fact that it's not possible.
SICP famously says "programs must be written for people to read, and only incidentally for machines to execute." And I can't help but feel, whenever I'm writing Go, that I'm spending far too much time telling the computer what to do – and nowhere near enough telling future developers what I wanted to do.
1) Ambivalence on Java roadmap, in my understanding (gradual build up, since after the Oracle's Sun acquisition). Even the earlier clean java docs, suffered from Oracle branding efforts. Downloading older versions were confusing via a long chain of navigation between sites.
2) Boredom after nearly a decade with Java programming.
3) Memory usage of Java in our conditions was much higher compared to other languages (C, C++, Go)
4) Not Java's problem, but whenever one thinks of hosting a HTTP service in Java, thinks of using a container (e.g. tomcat, jetty, Jboss etc.). Go seemed to have made making services very easy. Its possibly just a perception issue, but its there in my experience.
So we wanted to move, and Go looked stable at the time from all my HN readings. C was too bare bones(even string handling was a pain to me, after a gap), and even C++ would not have matched some of the things which Go has inbuilt. Few examples:
1) Json/XML parsing is the easiest with no work (or minimal) required, you can just have the field names capitalized (or use stereotypes) and it gets done, with a line of code.
2) Great HTTP client and server libraries, which make very easy to write your own standalone services, as well as write http crawlers. (I am quite excited that Go 1.6 will have HTTP/2 support for both, as per this birthday blog).
So, in nearly three years of usage, with largely no regrets[1]. It is what they say it is: a bare-bones, fast (both compile & run) and stable (hardly get any errors after you have coded stuff, one of the stablest programming paradigms in error handling, etc).
Thank you, Go team! Hoping to use it for many years, as default server side language.
[1] Some of the 3rd partly libraries, we use are still not ported over to Go. They have Java, C++/C versions.
What size team are you working with, that you were able to switch from Java to Go?
*
*
*
*
*
*
* *
* * * *
* * * * * *
(2) (1) (3) (4) (5) (all others)
Just to push a counterpoint, I never understood those startups with foosball tables, sponsored beer, team workaholidays on tropical islands but god forbid the work itself is any fun.
If a technology choice makes people enjoy their work more, learn new things, help them think differently and thus get more creative, then isn't that a big plus? Sure, maybe it does not weigh up to whatever downsides there are, but it counts! The whole idea that "fun" isn't allowed to be an important argument in a business decision feels horribly outdated to me.
* Mysql to MongoDB * PHP to Python * Javascript to Coffeescript
All because I was bored of the old tech.
and it made the site unmaintainable. I mostly blame the MongoDB and Coffeescript for that.
Now to be fair, I learned a ton and I am so glad for that experience, but I lost my website.
Here's another exmaple for you: Over the years, the project I work on has used the following:
vb .NET C# .NET
WCF asp Webforms MVC2 MVC4
The result is a maintenance headache (it's not a nightmare, but we do have to pause every time we unexpectedly encounter VB!), and that's where we've been disciplined enough to stay within the MS/ASP stack. Had developers been allowed to really go off-piste then we no doubt would have even more choices, and as a result be even less maintainable.
Yes, you might not be attractive to a certain subset of programmers if you are seen to be not using a trendy new language or framework, but there's the other side that by often switching you are left with a long laundry list you need to satisfy when you're recruiting so they can maintain the older parts. We already don't require vb experience, and we expect that people can pick it up well enough to maintain it, but a side-project in "go" might seem fun now, but if it becomes part of the business then you might find that in 5 years you have to take a bad choice in recruiting because otherwise you're left without anyone who can maintain that application.
I came to PHP late from proper languages circa 2008/2009ish been programming since I was a kid in the 80's, I've no great love for the language, it's purely a tool and once you ignore the rusty bits it's not a bad language for a lot of web stuff but I've never been concerned with purity/beauty for it's own sake I just care about what I can do in a language, these days I use PHP a lot on the web, Python for just about everything else (even my build system for automating browserify is in Python using Envoy) and I play with Go and such on the side.
All that and there is a lot of work in PHP clearing up after others (if you like those kinds of engineering problems which I do) which is also nice.
Heck, even for advanced analytics, I find mongo aggregates and mongo map reduce 10x more intuitive and SQL that inevitably ends up using zillions of non-portable tricks, stored procedures and god knows what. And atomicity and whatever else transactions guarantee you in theory can be easily emulated with some good db structure design tricks and app design tricks in practice.
And the smarter a RDBMS is, like Postgres, the more dangerous it becomes for maintainability: it's a zillion times easier to maintain 'logic expressed in app code' (because you have basic stuff like version control, tests and so on), than 'logic trapped in the db' (good luck explaining to a new developer how "X automagically happens because of trigger Y and stored procedure Z, so there is no app code for X that you can instantly tweak to change how it works"). This is why I at least have some respect for mysql: it's retarded enough that it forces you to put the login in the code, where it f belongs!
Really, give me a dumb document db like mongo any day! And if I want something "less dumb" there're things like arangodb and rethinkdb that can also do joins and graphs. And there're also "true graph-dbs" for index-less "joins"/traversals, like neo4j and orientdb, for when the relationships for when you actually end up doing more than 3 to 4 levels deep joins...
Imho the development of "high-end rdbms" like postgress and oracle has been a huge waste of human brainpower and all the benefits supposedly provided by these pieces of technology were actually from the clever app-level code that mostly worked around their inappropriateness...
Not just boredom but Java induced boredom. In case of Java boredom is symptom not the essence.
So, because there was some "ambivalence for the Java roadmap", a language with multiple implementations, a huge community (including open source), and so entrenched in the industry that will be there in 2100 too, you switched to a 3-4 year old language with tiny adoption compared to it (outside of echo chambers), an arbitrary roadmap as set by the core team which might involve anything coming the time for a 2.0 release, and whose majority of development depends on a handful of people Google pays their salaries?
1) I am not a functional language programmer. No disrespect, just stating fact, after having noticed a Lisp programmer having questioned my choice regarding "boredom". So languages like Lisp, etc were out, and had to look for a C like language.
2) My boredom with Java was also because of years of using, YMMV ofcourse. Also we were seeing issues in containers we hosted on, and that entire deployment paradigm seemed liked that of 2000s. As I said, in another reply, thankfully I had the luxury to choose, which is often missing in Enterprisey setups. And my co-programmers were also excited about it.
3) One thing which I forgot to mention in my parent comment is channels. It really appealed to me as a model, compared to the Barrier style of programming in Java's util.concurrent. We use that feature heavily in our now migrated Go services, and its ultra stable.
4) At the time of the switch, I wanted to move to a more bare-bones language, which is C-like. So did some benchmarks with C, C++ and Go. And Go was found to take a little more memory than C/C++, C was the least. But it was good enough. And as a successful validation of the move, I have been able to recently move to AWS compute instances from the standard instances (m3.large -> c3.xlarge to be precise). So we are getting more compute power, by paying roughly equal, because of lower memory requirements.
So I made due effort to find the right language, outside the "echo chambers" you see. And this "echo chamber" is also not so actually, honestly speaking, I learn a lot from guys like you on a regular basis :-)
edit: typo
As opposed to the brilliant stewardship of Larry Ellison?
Besides even if Larry said "kill it" tomorrow, Java would still survive -- so much that it's used in all kinds of enterprises.
If you took those away not to mention the message it would send to everyone would be enough to put the language in a death spiral. Good luck convincing management to use a language that even Google abandoned.
- Do all opensource projects require corporate sponsorship to be viable?
- Do Python, Ruby, even Rust have corporate backers that cannot widthdraw support?
- Does Java, being under the control of Oracle, present a better option?
- Many business are using VB, C# and F#, which Microsoft owns. F# especially could be abandoned, but it continues to find new use.
The Go code I write and compile today will continue to work for a significant period of time. If Google stopped paying the committers, this would not change - the language may not evolve, but this is a minor concern on a project of Go's popularity.
Some businesses still rely on COBOL and Fortran, but those are not exactly evolving either.
I personally think it is highly unlikely that Google would kill Go, but I do think one has to consider the possibility before betting the farm.
We're talking about decisions to adopt a language, so FUD and especially "uncertainty" is a very important factor to consider. This is not 1999 and Microsoft badmouthing Linux.
>Do all opensource projects require corporate sponsorship to be viable?
Not all of them, but a lot of them do. Especially languages. Even something like OCamL needs Jane Street and that french university a lot.
Go is not setup as even a 50-50 internal/external community. While the long tail is, well, long, the majority of commits and all of the steering is from Google devs.
>Do Python, Ruby, even Rust have corporate backers that cannot widthdraw support?
Rust has Mozilla and lots of paid programmers (all the core team for one). Without it, it would have gone nowhere.
Python and Ruby had had corporate sponsorship themselves too, but they have been far more organic (grass-roots) open source communities than Go from the start.
>Many business are using VB, C# and F#, which Microsoft owns. F# especially could be abandoned, but it continues to find new use.
Devs/Teams using F# are extremely few and far between compared to any established language such as C#, Java, etc. We're talking probably 2 orders of magnitude or more.
>Some businesses still rely on COBOL and Fortran, but those are not exactly evolving either.
Fortran is still evolving actually. Also Fortran and COBOL have been always mostly based on commercial compilers -- not community efforts, because that's how stuff worked back in the day.
And it's not like people write green projects in COBOL -- they just rely on it because they have huge important codebases that are 40-50 years old.
That's not the case with Go.
Not all (Yehuda and me, Huon), although both of us have been paid by Mozilla at some point in the past.
Go for the most part is written by a small team of paid contributors -- it's not certain at all that if they went away, other people would step in their shoes.
It is pretty inarguable that a large part of Go's success has been it's affiliation with Google.
I'm sure Java is 10x better now, but I'm not convinced it will be here in 2100.
Profitable code dies hard.
It is the lingua de franca of enterprise software development. In every sense it is the modern day Fortran/Cobol.
Client-server 4GLs were really starting to take off -- PowerBuilder, Visual Basic -- but those got washed away with the web.
That says something about the language to me.
You have a point but they seem pretty happy with their choice.
I believe that happy developers are more important than the tech stack.
dBase III derivatives.
4GL languages.
Yeah, where the owner is suing one of those multiple implementations, and playing silly buggers with J2EE certification. That really lends a fuckton of conifidence.
"2) Boredom after nearly a decade with Java programming."
Obligatory plug - if you're sick of writing out the struct definitions yourself, you can generate them from sample JSON: https://github.com/ChimeraCoder/gojson
This is especially useful when implementing REST servers/clients, because you can simply include an example JSON response which your tests use, and then use `go generate` to autogenerate the struct. Since they're both based on the same file, you don't have to worry about keeping them in sync - if there are any changes to the response structure, you only have to update it in one place.
/me tips hat to you :)
Go in my opinion was not mean for writing large/distributed web applications. But instead for writing tools (and fits very nicely in that space) and therefore replace C code base.
If I had to write something like docker, go of course would be a natural choice for it. But If I had to write the next big social network, well Java would be the natural choice.
Java has a much larger community, libraries, tools, ide and a whole ecosystem that makes Java and JVM a solid platform for writing application at scale (large team, large code base).
Back in 2009 #go-nuts seemed to be a much different place.
I write Go at work, and I admire many of the same things in Go I admire about Python.
I still wish generics were part of the language and will say their excuses about not being able to do it in a performant way seem to just be away of avoiding the subject.
Ocaml for instance, has a performant generics implementation.
Sometimes the Go community can have an anti-programming language research and anti-intellectual feeling which can be annoying, since in addition to Go I write a good amount of Haskell.
The tooling is nice as people say, however I think more maturing of the platform needs to be done. It's also never talked about how much slower Go's claim to fame of fast compilation got much slower after the 1.3-1.4 switch to a compiler totally in Go. In all fairness, I could be wrong about the last one since I haven't benchmarked it... but I can say it feels much slower than it was around 1.1/1.2.
Concurrency in Go is easy, however I feel like many erroneously think that channels or concurrency primitives like it only exist in Go. There are other languages with rich concurrency and parallelism options as well.
Using lots of interfaces and casting everywhere gets on my nerves since I like to have the strongest guarantees possible via static typing.
Overall though, I can't say I've had a bad experience with Go. I can say it feels like I'm using an okay tool (great in some places) with maintainers who put their hands over their ears to potential improvements (see the generics proposals over the years).
But yes, good point. I think a better type system should handle it and make it harder to drop errors on the floor. But that kind of asks for Rust's like sum-type which is too close to generics for their comfort.
I got around it by coding down to syscalls and allocating a goroutine to a simple poll() loop.
CSP via goroutines and channels is idiomatic Go, but it's not the only option. Go offers mutexes and other concurrency primitives which, along with goroutines as lightweight thread analogues, allow what you're looking for.
Locks exist, but you can't create a synchronized data structure. You can't create your own channel type because that's a generic thing which is reserved for the language authors, not the language users.
The whole point of modern programming is to create useful abstractions that allow a programmer to get things done without having to worry as much, and concurrency-related abstractions like parallel map-reduce and actors are powerful, but you have zero chance of making an abstraction for that in Go.
In Go it's basically CSP or bust; saying "you can use locks" is not an answer; locks are not a way to handle concurrency, they're a primitive for shooting yourself in the foot if you're unfortunate to be in a language where you can't abstract them away suitably.
type MyChannelType chan MyStruct ?
Are there other nice ones out there? Good to know...
Nobody can argue that generics in Go isn't extremely useful. After all, you couldn't have typed channels without generics. One has to be pretty obtuse to argue that the utility afforded by Go's internal generics wouldn't extend to the language as a whole; that somehow Go's standard library needs to be special.
If you look at the standard library, its authors had to jump through some serious hoops in many cases. Packages like "reflect" (the megatype Value), "builtin" and "sort", to pick a few, are a graveyard of typing awkwardness. The sort package and its special function just for sorting strings is practically a written confession.
Generics being a speedbump is an argument I haven't heard before. Can anyone comment on what the performance challenge is? Nim instantiates unique concrete type instances based on their parameters, couldn't Go do the same?
https://docs.google.com/document/d/1vrAy9gMpMoS3uaVphB32uVXX...
I wonder how the situation compares to Swift, Rust and Nim, three recent languages that have managed to implement generics without (as far as I know) a single complaint, and without descending into C++ madness.
I wonder how much the simple presence of an editing window on a language's home page contributes to overall adoption of the language. It sure lowers the bar to "I'll just write a few lines of code and see how this language feels."
More info here: http://blog.golang.org/playground
"This tour is built atop the Go Playground, a web service that runs on golang.org's servers.
The service receives a Go program, compiles, links, and runs the program inside a sandbox, then returns the output."
[0] - https://tour.golang.org/welcome/1 [1] - https://tour.golang.org/welcome/4
Let's hope this becomes a trend!
Having now developed 30 micro-services for Go, which all utilise channels and thus run much faster than any other language I used is just amazing.
Not only has my productivity increased majorly, but also my support time has rapidly decreased.
These micro-services which run in the background just work:
- With PHP they don't run out of memory or the database connection doesn't time out.
- With Python I don't get random crashes due to pool.map.
- With NodeJS I don't have to run multiple instances to get the speed.
I've looked at web frameworks like beego, gin, revel and others. But I'm waiting till something comes out that's more inline with what I have used with PHP. Something like slim framework.
If it ever does come out, it will give me the push to switch 100% to Go. Can't wait really.
Any public code examples ?
The stdlib with a couple convience functions really is quite good.
Since the TensorFlow video post is currently on the frontpage as well...as someone who uses neither C++ nor Go (I can write FizzBuzz level of code in both and read both well enough to get what's going on) I have to wonder how much internal buyin Go really has at Google if the core of such a project is implemented in C++. It's a project they see as valuable in the future and it would lend itself really well to Go as these calculations are done distributed and yet they picked C++.
[I'm not sure how much of the old framework they reused but if the will to Go was really strong I'm sure that wouldn't have been an issue]
and if you watch Jeff Dean's video, he mentioned more front end in other languages, Go is the only example and as the interest from internal.
What are your company's official languages used for?
[Former Googler around when Go got it's start]
Go is not Erlang. Go supports green threads (goroutines) and offers CSP-like channels to communicate between threads. However, it does not have a distributed computing story yet (at least, not compared to Erlang, the usual Java frameworks, etc.).
For a library such as TensorFlow, Go is not really an option yet, for many difference reasons. E.g. the lack of parametric polymorphism makes it hard to parametrize code for single, double, or even half precision. Go does not support anything like expression templates, which exploit laziness to eliminate temporaries. Moreover, a lot of machine learning happens on GPUs nowadays. AFAIK, there are no CuDNN bindings for Go yet.
tl;dr: impedance mismatch
As far as I know, goroutines are NOT green threads (threads managed by run-time environment instead of OS). Go produces native code (no VM). And, the goroutines can be multiplexed (depending on implementation) to OS level threads.
Green threads can be managed by a runtime library or VM, so no VM doesn't mean no green threads.
> the goroutines can be multiplexed (depending on implementation) to OS level threads.
Being multiplexed onto OS level threads is normal for green threads. Goroutines are "hybrid" (M:N) rather than "user-level" (N:1) threads, but anything other than 1:1 native threading means that you need a runtime or VM managing it, and its a kind of green threads.
I don't know that I'd say "just": like Erlang processes, goroutines have some special features not shared by all other green threads, even with a similar M:N threading model. I'd say that Erlang processes and goroutines are each specific kinds of M:N green threads with unique features and distinct names. So, its useful to distinguish them within the broader category, they just shouldn't be distinguished from the category.
That said, I started learning D at about the same time as Go, and for some reason D has attracted me more. Possibly the C ABI compliance (I was doing some JNI work at the time).
One question that I haven't found the answer to yet though is how well Go apps perform for long running processes, eg weeks or months? Have there been any issues around memory usage or resource handling? Any need for restarts?
I also write a little Go program every day as practice. If you want a sample of some Go. https://github.com/kris-s/daily-go
This is a common refrain amongst go proponents and I find it quite distasteful. It either implies those of us who prefer other languages aren't "getting things done" or those who feel productive in Go aren't smart/hard-working/educated/etc enough to "get things done" in other languages. I don't think either is true.
I think a more accurate way to look at Go is that the language, tools, and standard library make decent design trade-offs when your target software is 1) simple 2) network daemon-y or a CLI and 3) going to be worked on by a wide variety of developers. There are lots of problems that fall into that problem set, and it is very nice to have a language targeted at it, but Go is certainly not a good fit for a huge number of software projects. You will be less productive in those cases using it.
However, I guess the inability to deeply understand or pontificate about Go is an advantage in itself... forcing your only potential activity to be getting work done.
Also, are you sure that there are more popular Go projects than popular Scala projects?
The syntax perhaps. In reality there are a large number of concepts and idioms to learn on the never-ending road to really becoming proficient.
Take initialization for example: look at all the edge cases belabored in section 8.5 of the C++14 spec, or even the intricacies of initialization in a simpler language like Python. Now contrast this with how much lexical and conceptual space it takes to describe zero initialization in Go's spec: https://golang.org/ref/spec#The_zero_value
As in other areas, Go's approach here has downsides. (Possibly major ones.) But those tend to be conscious design decisions to minimize the total number of concepts and idioms present in the language.
JS is pretty damn tricky though, I'll give you that one.
It's subjective though. I'd guess that if you're a fan of the suckless style of engineering, go might be right in your wheelhouse and you will probably find it very beautiful if you can accept garbage collection.
Also, I don't understand the benefit of being able to learn a language in one day. You can learn how promises work in a single day and then apply that to any language that implements them. With Go, you will learn the language but then have to re-implement promises for every type that you have [0].
Consider the use case of issuing two API requests concurrently and assembling their results. You can do this using the fan-out, fan-in pattern described at http://blog.golang.org/pipelines
Search the page for "func merge". Any time you want to fan out and fan in, you will have to write that block of code for the types of the channels you have, or else use interface{} and lose type safety. So the cost of a language that can be learned in one day is that every single time you perform this very common concurrency pattern, you will have to repeat this code and potentially make errors in the process. And if you want to change how this merge pattern works across your codebase, you will have to change it in many places. What happened to DRY and reusability?
[0] There is a Go promises library, but it uses interface{} for all callbacks and does not appear to have been an active project over the past year. https://github.com/fanliao/go-promise
So, just like VB then?
gofmt, for instance, is uncontroversial. Taking the decisions about how to format code away from developers and standardizing it is widely seen as a win (a win Python flirts with as well).
Well, there's a lot of other things in Go that have been pre-decided for you, not just the formatting. The net effect is that you don't have to waste time:
* thinking about designing a DSL for your programming problem (DSLs in Go require parsers)
* designing a class hierarchy to express your programming problem
* choosing between event-loops and callbacks or pools of threads
* picking the right associative container library (do I want a red-black tree? a hash table? a trie?) for routine coding problems
These pre-decisions can feel confining, but I think for a lot of developers Go reveals that those decisions were usually a waste of time. When you actually hit a place where you need a red-black tree, it's not that big of a deal in Go to bring one in. You're just not going to do that for your session store or for a simple lookup cache --- which is, I think, what a lot of people who can't stand Go's lack of generics would be doing.
The problem, I have with the sentiment (which I grant is largely me over reading into it) is that a lot of the things people think of as noodling or a waste of time, are the central problems in more complex environments. Making sure your program is correct is very important in some environments. Making it easy to express complex domain knowledge as a subset of a programming language is a huge win in some environments.
That isn't a lack of getting things done, thats just more complex logic requirements.
But in C++, those are decisions you might make in parsing a config file, or in managing a simple table of sessions, or adding an LRU cache to something. You can't get away from the decisions. I have an array of stuff, and I have to decide, "do I want a list, an slist, a vector, or a deque, and what the fuck is a deque?". 98% of the time there is one sane decision that is so close to optimal that it's not worth tinkering with.
In Go, for prosaic, routine code, those decisions have been made for you. You have to go just annoyingly enough out of your way to second-guess those decisions that you almost never do, and you're almost always better off for not having to do it.
I'm currently working on a project (WebRender) in which we keep using the standard library hash table in the first cut of code whenever we need an associative lookup table, and every time we use it it keeps coming up #1 in the profile. Almost every single time. We then have to switch to another, more optimized data structure, and generics really help here so that we can reuse these data structures. In fact, if we didn't have them, we'd probably be sunk.
This just isn't as much of a big deal as people think it is. Go makes it a drag to use a custom container, but it doesn't make it hard, and that might be how it should be!
> This just isn't as much of a big deal as people think it is. Go makes it a drag to use a custom container, but it doesn't make it hard, and that might be how it should be!
I believe you that it wasn't a big deal in your case. But I disagree with your general claim. I think you're extrapolating from your use case to claim that generics aren't important (vital, in some cases). I'm saying that, in my use case, they are.
If the default doesn't work for some reason then I can tackle that problem but at that point it usually is core to the problem I'm working on.
For some people Go is anything but frictionless though. They spend all their time in Go fuming that their favorite abstraction isn't there. For those people my experience doesn't translate.
Otherwise, the go built-ins are usually the "get on with it" win.
It could just be the feeling that Go gets out of your way more than other languages do. (For example, people complain about how much ceremony is involved in writing Java. Go could easily feel like "getting things done" in contrast.)
I'm honestly very happy with how straightforward it was, even lacking generics---I solved the problem of no generics by basing the resource creation and management on JSON and a "Storable" interface that just defines key() and newEmpty() methods (so I have to write five lines of boilerplate for every struct that will be a resource... shrug). I didn't parameterize the storage logic, basing it on only a single NoSQL backend (BoltDB); however, parameterizing that side shouldn't be harder than wrapping the database access behind another interface.
I like the ability to switch between tight static typechecking and freer runtime typechecking on-the-fly, without having to build a large, complicated framework for either within the language. It may not be the most innovative language of the decade, but I think it's a pretty good combination of features for practical, high-performance web server development.
It's not cleaned up for easy reading (hell, I haven't even run `go fmt` on it ;) ). But it has unit tests and seems to work. ;)
One thing worth noting is that it relies on the Storables to also be tagged with necessary JSON. It occurs to me that I could also have used tags instead of the key() function in the interface to specify which field in the Storable should be the resource name; key() is a bit more flexible in that the function could calculate the name using any method though.
You can see the comments here: https://gist.github.com/TheDong/ce1d7b86b88c86d972b6/revisio...
You should never post code without a copyright license or disclaimer attached and I'd appreciate if you rectified that forthwith.
The advantage to the Storable is that other parts of the program can create and store those resources without needing knowledge of how keys are constructed. I also can't think of a way to do what empty() does without making it an interface function; "I need another copy of this unknown interface type" isn't something go has a clear solution for. Hypothetically, I could make empty() into a function that returns interface{}, take the key() suggestions you've given (though that leaks key-making knowledge), and then the whole thing would operate on interface{} type... Doable and more flexible, but not quite as clear on what resource will actually be stored and retrieved. Good tradeoffs.
A bit of feedback on feedback: avoid adjectives like "dumb" and "stupid." The signal-to-noise ratio is too low on them, and it makes for a harsher community (as other commenters in this topic have mentioned on their experiences trying to get help).
Have been using Go for the last few years, from backend, tools, and even complex scripting type of tasks (for things more than I would like to do in Bash). Start using Go for game development as sideline projects recently, it is fun.
Granted there are things I would like to see may not on the road map, but I am very happy with what we have now, the great tools, and the great community.
Keep up good work and looking forward to the future releases!
I prefer C99. A smattering of features I like:
* Variable Length Arrays
* Variadic Macros
* Designated Initializers
* Anonymous Structs
* Compound Literals
Some of the meta-"features" I like: * No built-in runtime or GC
* A choice of dynamic vs static compilation
* A good libc is wonderful but not necessary to get work done fast
* The tooling is mature and plentiful
I'm sure Go is a fine language. I probably suffer from some sort of first-language bias as I've been using C for a long time and only know a smattering of Go from toy programs, blog posts, and reading the source to interesting projects like CockroachDB.1. Variable Length Arrays: Go has slices 2. Anonymous Structs: Go has these 3. No built-in runtime (in C): This is not true unless you are compiling -ffreestanding -nostdlib, etc. I have done this in C, but such programs have to call system-calls directly and provide their own malloc, etc.
- Gorp[1] for SQL stuff (I like that it "encourages" your program data structure to reflect your DB structure) - amber[2] for templating - gorilla/mux[3] for routing - gorilla/securecookie[4] for login/auth management (they have a sessions and context library too if you want that)
Go also has libraries that handle basic versions of most of these tasks (and these third-party libraries extend them). If you're building something simple they might be sufficient.
[1] https://github.com/go-gorp/gorp [2] https://github.com/eknkc/amber/ [3] http://www.gorillatoolkit.org/pkg/mux [4] http://www.gorillatoolkit.org/pkg/securecookie
Maybe it's personal preference, but there's something more satisfying about the modular approach of building up an app using only components I need and understand, rather than starting with a magical feature-laden framework but quickly realizing you don't need half of what it does. I think Go aligns particularly well with this philosophy given its emphasis on composition.
Not to mention that since goroutines cannot be forcibly terminated, it's the only way to control the lifetime of a handler (e.g. applying timeouts).
It's much more useful to read a competent critique of Go (of which there are a couple) than post after post saying how everything is great. Everything can't be great, it's a basic fact of software engineering!
So a master woodcarver works with chisels, his "tools", for his entire life...30 years, and with them he turns bare wood into incredible works that the world recognizes as valuable art.
Someone hands him a chainsaw, which by all measurable metrics should allow him to do the work 10x faster because, of course, it is more powerful, modern, and designed by "superior minds" whom understand IC engines, gearing, etc etc.
Does it? And should he switch?
Every time I peak into the source of a standard Go package, I'm blown away by how clean and simple the code is -- and it's a fantastic way to gain a deeper understanding of the language.
That has mostly not been my experience diving into std C library code, or quasi-standard stuff like boost...
Oh and compared to Go, I always point to sorting in Go. Just look up the library. I think it's awfully complicated, compared to JavaScript where you simply pass a comparator function. In Go you need a lot of lines to achieve the same thing.
I didn't realize how much I disliked JS playing "loose and fast" until I used Go with its rigidity.
In the beginning, those Node.JS codebases were clean and manageable. Then came bug fixes, changing requirements and refactors. 2 years worth of that led to something that was quickly becoming unmanageable.
But then came Go and, after a rewrite, everything was clean again! It doesn't matter than the code base is only 3 months old, I'm sure it's going to stay this clean forever!
But hey, should those Go codebases become messy a couple of years from now, I'm sure Rust will have matured into a worthy successor and, after a rewrite, we'll all have the cleanest codebases ever!
Its incredible how much easier things get when you need to refactor if you have types.
Then you could, for example, browse the ed column of birthday wishes & happy usage stories of Go, say on the left hand side, and then also, scroll through the op-ed right column of dissenters and critiques.
There's just something very disheartening about having a bunch of "anti X" dominating what you were hoping would be a discussion of "X"; downvoting is certainly not the right solution -- it's fine to be anti-X, it just needs a little bit of its own space.
My idea is different, though -- the essence is
(1) you'd self-tag -- imagine clicking 'in support' or 'in opposition' to comment.
And then (2), those two different sets of comments would go into different parts of the page.
Maybe a two-column view? Maybe separate pages?
Basically, conversations in a single-section format like HN are constantly getting overrun by criticism. Criticism is interesting and has its place, but it tends to be noiser, and it's disheartening when it's getting in the way of something you want to be excited about.
I was super excited for the cross compilation abilities on Go 1.5 and now I have something else to be super excited for in Go 1.6, can't wait!
It would seem the concurency model of go would be a great fit for a lot of those existing python/perl tools. My searchingg for projects show it hasn't really taken off in this domain. I might start rolling my own packages.
How is the package management in Go? Data structures and String manipulation? And is does that concurency scale across nodes (al la MPI)?
Tripal I had never heard of. It uses "Chado" db schema which I have heard of (though flybase) which while flexible isn't always performant.
Performance gains and simplified development are so appealing (I have 12 tools to maintain in Java/perl/php)..
Biogo is what I was looking for. https://github.com/biogo/biogo
Dan (the lead developer of biogo) is very competent and I'm sure would love bug reports and PRs in biogo as well.
We're trying! https://groups.google.com/forum/#!forum/gonum-dev github.com/gonum . Bug reports and contributions encouraged.
I think Go has a very limited use case, but it is incredibly good for that use case. If I had to define it right now it would be along the lines of "Highly concurrent micro-services (re: small code bases) that requires decent performance (memory + speed) in a somewhat distributed fashion". Obviously it's not perfect but it's the feeling I've developed recently.
There are alternatives but most of them aren't backed by a big corp like Google, so they will have to succeed in their respective niche before getting widely adopted. I wish that wasn't the case cause languages like D,Nim or Crystal are really good and way more enjoyable to write in my opinion. Out of all of these(including Go) one will be the next big language, no question as people move from dynamic or VM backed languages (for good reasons).
I replaced python with go in my projects and i'm so happy!!!
What the heck are you talking about. Who uses Swift on this sort of scale? This is just one of the many MANY large companies using Go to power their core infrastructure.
Node is another technology I would put ahead of Go as far as adoption. Maybe Go will get better, to me, it's too different than a lot of what I see day to day with Java, C#, Python, JavaScript and on and on. The part I love about Go is that it handles garbage collection, other than that, it's atrocious looking C like code.
Then name any company using Swift to the level Dropbox uses Go, or retract your claim.
Go code looks nothing like C other than general brace styling. I can't decide if you're trolling or just genuinely this ignorant.
They still use Python if you read down the Twitter thread, but I can name so many more. Cloudflare for example.
The likely reason you see such a difference is that the languages are suited to different things. Swift for example is all but mandatory for iOS apps, and so this is not adoption but migration. If you're just looking for small freelance projects then I expect Go will be a long time coming, but if you're looking for large infrastructure work you'll find an awful lot of people very interested in Go.
I disagree. The one thing that makes Go code look more like C than other C descendants like Java, C#, JavaScript, Swift and even to some degree C++ is its error handling style. Having a seperate error value is of course a big improvement over C but visually the error checking code following many statements appears a lot like C.
And the second reason is that Go has pointers with a syntax modelled after C.
So in the eyes of the 90% or so developers who have been using mostly Java, JavaScript and C# for 15 or 20 years, Go has to look a lot like C.
> I can't decide if you're trolling or just genuinely this ignorant.
Stuff like this breaks the HN guidelines. Please edit it out of your comments when posting here.
How much more would you like me to blunt what I say in order that it doesn't violate unclear rules?
Any substantive point you have will become sharper once you edit such rudeness out, so it isn't a question of "blunting", but of being respectful. Even if you don't respect the person you're talking to, you need to respect the community by holding yourself to a higher standard.
>Apple has made it clear that you'd better learn Swift
Can anyone point to any statments to this effect by a leader at Apple? I haven't found any with a web search.
Who cares about adoption rate? Is PHP the best language to make websites because of it's popularity? Hell no!
I tell you what I care (and I know I will eventually pick Go for these reasons):
- concurrency - parallelism - stability - speed - community - libraries - proper use of hardware - small footprint