Yes! Two modern languages that are 1) statically typed and 2) statically compiled. At least some people still get it that "modern" and "nice to use" does not implies scripting-like dynamically-fancy typed language "à la" PHP/Python/Javascript/...
And the author did not mention Playground for Swift which is just incredibly useful to start experimenting with the language!
(Edit: of course Rust is another one as well, but I was in the context of the article)
Note that modern does not mean perfect.
I'm not sure you answer could not fall into the "this-language-lacks-my-favorite-cool-fancy-feature-so-it-is-necessarily-an-old-fashioned-crappy-piece-of-s*" syndrome?
Well it does have a structural type system (one of the few other languages with one being OCaml, '96), a GC (popularised/made acceptable by Java '95) and provides language-level support for CSP (Occam '83)
> Go compiles very quickly, but that actually didn't impact us very much since we have largely been using interpreted languages on the backend. I'll betcha those poor Java programmers get pretty excited about this, though!
Someone should probably mention to the author that us poor Java programmers have had incremental compilers for over a decade, so compilation speed hasn't been an issue for a long time.
Best not to mention that we've also had generics for over a decade, though, wouldn't want to upset him.
Well, you can consider it mentioned to the author... that would be me!
I shouldn't have tweaked Java so mercilessly since I did love the language for so long (lo, these many years ago). However, I do think you really underestimate the compile speed of Go. Even if 99% of your builds are incremental, you can't entirely discount the occasional clean build. While it has been many years since I hacked Java, even incremental builds could take more than a few seconds. Even a clean build on our 50 kloc Go codebase clocks in under 2 seconds (0.1s when the disk caches are pre-warmed).
As for generics, I do like them, and seeing them on iOS (with Swift) is a pretty big win. I'm not entirely convinced that Go is much poorer without them, however. The type system in Go is really unique; if you tried to program Go using Java-style paradigms, there's no question that you'd find it pretty disappointing. However, Go does allow some new ways of thinking about types that allow a degree of flexibility that simply can't be expressed with formal class hierarchies.
If what you're doing works for you, just keep on truckin'. But if you give some of these new languages a chance, you might find the areas where they really shine. We did!
or TP-Win vs Turbo-C-Win. Pascal compilation was "don't blink, or you'll miss it" vs "go get coffee...".
Go reminds me a lot of TP with garbage collection, dressed up to look like C to avoid bruising Bell Labs egos. (OK, closures and multiple return values are pretty cool, too, as are associative arrays and array slices)
Perhaps not 2 seconds, but I just checked and a 41,000 line Java codebase I work on compiles from cold in 3.4 seconds.
I'd never bothered with incremental compiling because the build times are so low, but I just tried now and changing 1 java file leads to a 0.3 second rebuild.
If you want to contrast compile speeds, try Go vs. C++. Java compilation has been very fast for a long time, as others point out.
For my part, my main beef with Go isn't necessarily the lack of generics, but the obstinate lack of expressiveness. It's back to Java or Python where you're forced to break up your code into discrete, imperative chunks instead of chaining stuff together in elegant flows. It's like Go's authors missed out on functional programming. No pattern maching, which seems like a huge miss considering Go has select { }. Go's syntax is, in many ways, even more rigid than both Java and Python.
That rigidity extends to error handling. While I agree with the philosophy behind Go's rejection of exceptions, I don't agree with how it's been implemented. In discussions about Go people always talk about exceptions vs. explicit error returns, but hardly anyone mentions the fact that error handling completely takes over our code: errs are everywhere!
A concrete example: Go has := for type inference, but it turns out you can almost never use it, because almost every function needs an "err" that you end up declaring. Often you start out like this:
Quite elegant. But then you need to add some more code, and you actually can't rely on type inference anymore:
var err error
var result *Result
if result, err = getResults(); err != nil {
return
}
stats, err = computeStats(result)
It turns out that just because you needed another "err", you had to rewrite the statement, which is frankly ridiculous.
The next problem here is that we can't simply this:
result, err := computeStats(getResults())
That's because computeStats() takes a Result, not two arguments (Result, error). In functional languages, this is elegantly solved through monads, but not in Go; you can't "short circuit" function chains that might return errors. There goes your expressiveness.
Another problem that the error phenomenon infects the language with is that you can't have global variables initialized this way:
var spaces := regexp.Compile(`^foo`)
To get around this, the regexp module defines an alternative function:
var spaces := regexp.MustCompile(`^foo`)
(Never mind the fact that you're not allowed to declare this as a const. It is a constant, I want it to be a constant, not a global variable!)
The fact that almost every function ends up having an "err" around raises the question: Why is it not an integral part of the language in the first place? Why do I have to declare err?
Other languages (Swift among them) fix this problem through sum types: The function can return a value which is either a real value or an error. Go's idea of returning a value and an error is logically nonsensical in almost every case, because the error is used to signify that the value isn't available due to failure. I'm not a language-theory purist who thinks everyone should really be using Haskell; these are practical concerns.
Overall, Go does feels disturbingly warty in places, which is incredible for a new, clean-slate language. Favourite wart: interface types being magically pointer-based, leading to the whole non-nil value being nil idiocy; it's mindblowing that they got this so wrong.
I liked Go a lot better before I started using it.
The error handling stuff more than lack of generics is my single biggest beef with the language. It is basically C-style error handling from the 80s all over again, only you return error codes as a separate multi-return value.
The Maybe monad style is so much cleaner, it also allows Elvis operator-like changing, e.g.
See my comment above for why I don't find this to be that great for server code. If each of those getOrElse() is an actual potential runtime failure (as opposed to just something that could in theory be nil, but you know won't be, by construction), then it's something you're going to need to deal with explicitly.
E.g., think of each of those method calls as being a call to some external system (filesystem, memcache, database, etc) that might fail, even if the code's correct. Throwing an exception is usually a pretty bad strategy for those cases if you want to able to sort out what went wrong later.
> If each of those getOrElse() is an actual potential runtime failure (as opposed to just something that could in theory be nil, but you know won't be, by construction), then it's something you're going to need to deal with explicitly.
getOrElse is a function that is used for exactly that, i.e. for things which can fail in the sense that a parser failed to parse some text because there was a syntax error in the string, or the Map datastructure didn't contain the given key so it returns "nil" or "None" or whatever. If you had something that you know wasn't supposed to be able to fail, then you would probably just go ahead and assume that it can't fail, and let the program error out if it does fail since then you've revealed a bug. To use getOrElse for things that shouldn't fail to begin with is not good.
I don't know what you mean by "deal with it explicitly", since getOrElse indeed does deal with it explicitly.
> E.g., think of each of those method calls as being a call to some external system (filesystem, memcache, database, etc) that might fail, even if the code's correct. Throwing an exception is usually a pretty bad strategy for those cases if you want to able to sort out what went wrong later.
getOrElse doesn't throw an exception. It returns "null" or "None" or whatever value is supposed to represent "does not exist" in the case of some failure (failure here being "produced none/nil/null"). It has nothing to do with throwing (or catching) exceptions.
Sorry, I wasn't entirely clear -- I was referring to the original basis for the complaint about verbose error handling. I'm intentionally separating the handling of runtime errors (e.g., memcache request fail) from validation errors (garbage data causes a map to have a nil entry) and unexpected errors (whoops, that shouldn't have been nil).
For runtime errors, as I've stated above, I prefer explicit handling close to the error site. I think this is the right tradeoff for server code, though I'm less certain for client code.
For validation errors, I prefer, when possible, to have a parser that either succeeds or fails as a whole, and whose output I can then rely upon to be properly constructed.
For unexpected nils, I do like the option-type/monad approach, though in most cases (e.g., when writing Go, Java, C++, or Javascript) I just let them NPE/segfault.
Note that only one of these cases -- runtime errors -- results in a (result, err) pair in Go. The other two are just about result checking.
> I'm intentionally separating the handling of runtime errors (e.g., memcache request fail) from validation errors (garbage data causes a map to have a nil entry) and unexpected errors (whoops, that shouldn't have been nil).
I think that the original poster (the one you responded to originally) was talking about errors in the first two senses; things that you should/want to handle yourself. I guess the last thing should be handled with a panic?
> For unexpected nils, I do like the option-type/monad approach, though in most cases (e.g., when writing Go, Java, C++, or Javascript) I just let them NPE/segfault.
It doesn't that you understand idiomatic uses of option-type. Their used for things that legitimately, as a part of the normal operation of the program, can be "null". Not for things that should really not be null. At least I assume that uses of NPE/segfault is not typically used for things that might be null (like returning null from a Map since the value is not there). So, they are not used to "hide" null pointers that shouldn't be null to begin with.
For cases where a nullable type, or Option[T] if you will, really should not be null, it would be more idiomatic to "forcefully" extract the value. In other words, use a function that returns the value, or throw an exception if it really isn't a value (it is null); throwing an exception here would be an indication of a bug. But this use is usually thought of as unidiomatic in languages like Haskell: you should rather make sure that you don't have to "forcefully extract" things like that to begin with.
> I think that the original poster (the one you responded to originally) was talking about errors in the first two senses; things that you should/want to handle yourself. I guess the last thing should be handled with a panic?
Right -- I'm only saying that I want to handle the first runtime errors explicitly at the call-site. This is where I explicitly like Go's error style. Other approaches to this problem (e.g., pattern matching) have been discussed elsewhere on this thread, and that's fine for languages that want to go down this route, but the extra language complexity is a tradeoff. I can see coming down on either side of said tradeoff, but it's not cut-and-dried.
> It doesn't that you understand idiomatic uses of option-type [...]
Sorry, that came out wrong. Where the option-type/elvis-operator approach comes up, it seems, is when you need to dig a few levels deep through possibly-nil references, as in cromwellian's .getOrElse(foo).getOrElse(bar) example. I certainly understand how that can be useful, but in my experience it seems to come up most often when dealing with unvalidated inputs (I'm sure there are other cases I'm not thinking of; this is just my personal experience). Whenever possible, I tend to prefer having the inputs parsed, validated, and either accepted or rejected, by a validating parser of some kind. Then I really can just assume it won't segfault as I read through these chained methods/fields.
> For unexpected nils, I do like the option-type/monad approach, though in most cases (e.g., when writing Go, Java, C++, or Javascript) I just let them NPE/segfault.
.. segfault some unknown distance from the place they originated. I mostly do Java, with a substantial dash of JavaScript and Ruby, and in all of those languages, i waste time tracking nulls to their source. A language which blew up as soon as an unacceptable null arose would be huge win.
Actually, having moved from entirely Java to only mostly Java, one thing i've noticed is that in dynamic languages, wrongly-typed objects can propagate as freely as nulls. I spent an unforgivable fraction of today tracking down a bug in a JavaScript app where a framework was passing the wrong type of model to a view. In a strongly-typed language, that would have failed as soon as the framework did that (or perhaps even at compile time), but in JS, it failed in a cryptic way hundreds of statements later.
For extra comedy value, it turned out that the reason the framework was doing that was because i had passed a null into one of its configuration properties - because i'd innocently written something like:
And since the views are defined in files cartView.js and itemView.js, and since the files are loaded in alphabetical order (because we're just throwing them out of Rails and not using RequireJS or similar, i know, i know), at the point at which CartView is defined, Initech.Commerce.ItemView is null!
Basically, JavaScript is a language only a Dwarf Fortress fan could love. Whereas i see Go as more suitable for Minecraft fans.
My personal experience at a startup with a large amount of Go code running its frontend and backend servers. YMMV.
First off, I also find the interface-nil thing to be a frustrating edge-case. I also know that this design was the result of some difficult tradeoffs, as such things often are. We can argue about whether they made the right tradeoff, but I don't think it's fair to refer to it as a "mindblowingly wrong idiocy".
Second, on error handling. Having now written a large amount of server code in Go, I find that I strongly support their approach, even when I find it a bit verbose. Here's how I find it actually plays out in practice:
// You write something like this:
result, err := getResults()
if err != nil {
return err
}
// Then you reuse err for the next call
result, err := getResults()
if err != nil {
return err
}
stats, err := computeStats(result)
if err != nil {
return err
}
Eventually, you realize that `return err` just isn't enough most of the time, because it's impossible to make sense out of your logs. So we added a simple "error chaining" function that allows you to pass context when you return the error, which makes the logs much clearer than just a single error message, or raw stack trace. And of course it is often the case that you want to do more logging in your error blocks, even as you ignore the error and move on (e.g., "spurious error reading memcache entry; falling back to the slow thing").
The practical reality of writing good server code is that exceptions which get caught way up the stack are inscrutable in your logs (at least that was our experience), so it makes sense to know up-front exactly where failures can happen, and to be forced to think about them the first time you write your code. Yes, it can be a bit verbose, but we've found the tradeoff to be net positive.
For the record, I don't think the interface problem is "mindblowingly wrong idiocy" (you words!).
But I think it's a good indicator of how parts of Go's design are flawed from the outset. Design is hard to change later, so it's important to get it right from the start.
Again, I totally buy explicit error propagation. I just think Go's solution ends up cluttering your code. It's so focused on errors, yet handling isn't a first-class construct, and the mechanisms it gives you for dealing with errors aren't very good. You have the cast check (a, ok := ...) and you can do a type switch (switch a := err.(type) { ... }), but it's rather weak for something that permeates every corner of the language. At least us have pattern matching.
I find the stack frame problem disappointing. It's amazing that there are now several libraries to create artificial stack frames (eg., https://github.com/facebookgo/stack) just so you get this.
I don't want to pick nits too much here, but "[...] leading to the whole non-nil value being nil idiocy; it's mindblowing that they got this so wrong." So I paraphrased a bit :)
I understand that some may find the error handling a bit too verbose, but humbly submit that there's a legitimate tradeoff to be made in terms of language complexity vs. verbosity, and the Go team generally tends to fall on the side of simplicity over tersity. This works well for us, but not everyone will find it palatable. YMMV and all that.
The stack frame "problem" doesn't seem all that bad to me. It would be nice to have a built-in solution, but our own code for dealing with this is one tiny file someone banged out in a couple of hours, so I'd hardly call it more than a stumbling block. After watching the Java world deal indefinitely with untold design flaws in core libraries, I support keeping things simple at the outset wherever possible.
But hey, there are lots of choices out there, so I'm not telling anyone they must write their servers in Go. Just that it works well for us.
Also better not mention that the Java debugger understands structures/types just fine. Had that for a decade or so as well.
It's all so cute being a startup who hasn't released anything. All of that operational nonsense like remote debugging/monitoring, log management, hot reloading etc just isn't an issue (yet).
How does a language without generics claim to "do more with less code"? Don't get me wrong, I like Go, but it's hard to get behind any language with that low of an abstraction ceiling. Then again, I work in a web shop, building CRUD apps for a living, so maybe it doesn't matter for people who only have to build one version of something.
As a web developer, my tinkerings with Go haven't ever left me wanting for generics. I can't say that this would be the case for every usage case, but it was never an issue for me personally.
It's not really my place to argue for or against them, just figured I'd share an anecdote.
Sure. And that era of Java was responsible for much of the crap, duplicated code that many of us enterprise developers have had to maintain for a living.
To ask developers to go through that again is a bit rich.
I saw a transcript of Rob Pike's "Less is More" where he says
"What it says is that he finds writing containers like lists of ints and maps of strings an unbearable burden. I find that an odd claim. I spend very little of my programming time struggling with those issues, even in languages without generic types."
and I had literally not three days before fixed a bug in somebody's c++ code caused by using their own hand-rolled stack class instead of std::stack, so I'm not inclied to be charitable on go's lack of generics.
Go is really minimalistic.thus,you either follow its philosophy or you cant use it.That's why I believe this language isnt for everyone.Try to look for alternatives such as D if you want a concurrent "safe" C ,which doesnt eat as much memory as the JVM.
As much as I dislike that most of the "language shootout" benchmarks are mostly numeric wanking, the binary tree benchmark actually makes, uses and disposes of quite a few data structures, and seems more like a real application program to me.
No, the Go optimizing isn't great. This will be addressed after the compiler is completely rewritten from C to Go. The Go community is growing too, which is probably more important than how well you do on a micro benchmark.
I don't understand how writing the Go compiler from scratch in Go is going to make it better than super-mature compilers. Wouldn't it better to leverage Clang/LLVM? Redoing all the optimizations by rewriting the whole compiler in Go seems like it will significantly delay performance parity.
I don't think being written in Go will make the compiler produce intrinsically fast code. Rather, it will be easier for them to iterate. It is much faster to develop in Go than in C.
They could use the same front end and enable optimisation passes in the backend with compile flags / heuristics. GHC for example uses for certain flow optimisations "fuel" once it runs out it stops doing further optimisations. For long running applications, ideally the runtime would profile the application during its execution and JIT / optimise based on the live state of the application. The JVM demonstrates that this can have major benefits over AOT compilation.
No, going fast is about not doing work twice. It is about the dependency graph. Go was developed to make dependency analysis very simple, so you recompile a tiny number of files even on massive, complex apps... and don't spend massive amounts of time deciding what to compile, or building stuff up just to tear it down (include all the things, use macros to exclude, etc).
. Neither of these contradict your implicit point of "they chose to invest time in this port instead of trying to maximize performance ASAP", but you still might want to know.
Seems weird to fork a compiler like gcc or clang via transpilation, because you split the community. It may be faster to iterate on Go, but will all of the compiler contributors on GCC or Clang/LLVM want to switch to editing the go version?
It seems you'd lose out on picking up improvements from the main compiler branch and have to keep transpiling and gardening in patches. Plus upstreaming improvements would also be irritating.
I'd think a Go frontend to LLVM would be optimal in terms of productivity.
The go compiler is based on the plan9 c compiler, not gcc. I'm not quite sure what the story is with gccgo (who initiated it, why) - but i think it was for interfacing with c on non-plan9 architectures, and not really about leveraging gcc optimizations.
Why only compare based upon one of the benchmarks (binary-trees), and also why choose 32-bit which the most unlikely architecture this would run on as I see it ?
Here's java compared to go on all benchmarks, 64-bit:
I picked the 32 bit version of the benchmarks, since the demo program didn't need multiple GB worth of memory, FWIW.
I thought that single vs multi-core was comparable on either 32 or 64 bit.
Otherwise, I thought I was pretty clear why I picked binary tree over, say, mandelbrot set. Wanted to favor "data structure" usage over number crunching.
>I picked the 32 bit version of the benchmarks, since the demo program didn't need multiple GB worth of memory, FWIW.
More addressable memory is but one advantage of 64-bit versus 32-bit, the fact that it has double the amount of registers is another VERY important performance aspect since registers are by far the fastest place to store and retrieve data from, so less need to push values from registers on to stack / flushing cache lines, means faster running code, and in hot loops the difference in performance can be huge.
>Otherwise, I thought I was pretty clear why I picked binary tree over, say, mandelbrot set.
But why not show ALL the results and just highlight binary trees, there are a lot more benchmarks there than mandelbrot, also I don't see what is wrong with 'number crunching' benchmarks ?
Most business apps don't do much number crunching or bit-twiddling. Think of moving strings/records/objects to and from a data store, putting them temporarily in an in-memory hierarchy or an indexed cache for lookup/evaluation, then pumping more strings to a browser / the console / a dialog box.
I didn't pick the binary tree example to pick on Go, it's my "best match use case" for the "allocate and find things" that I envision goes on in most applications, regardless of which languages I am comparing.
Rust still isn't 1.0. In fact to even use basic syntax such as selecting a range of a slice you need to specify that you are using not yet supported features.
I love the concept of rust, but trying to write a trivial program in it was a pain in the ass.
Thanks for the tip. Everything I read about Rust sounds really good, BUT, I've yet to download it and write Hello World, so not sure how painful it is in practice.
It's not painful, the problem is that the code you write now might change in a month. With the release of 1.0 (which is less than six months IIRC), they will stabilize the syntax (but will be adding more down the line). But the libraries will still be very much in flux.
The reason things have been changing so much is mainly to get a better language now, and not have to write a 2.0 that changes everything. When you look at rust code from six months ago, things have improved greatly, so it will hopefully be worth it.
> I'm pretty darned sure that Rob Pike (creator of Go) and Chris Lattner (creator of Swift) know a few things about computer languages that the rest of us haven't sussed out yet. If these guys think the world needs a new language, it's worth finding out why. (And if you do know more about programming languages than these two? Well, it's honor to have you reading my article, Mr. Wall!)
Larry Wall is the ne plus ultra of programming language design. This is satire, right? Please tell me this is satire.
Is that not the point of his remark? As I understand him, he's implying that Larry Wall is the only one who knows more about language design than Rob Pike and Chris Lattner. Therefore, we should listen to Rob and Chris.
Or maybe it's your comment I'm misunderstanding?
Edit: Ah. It's been pointed out that perhaps your own remark about Larry Wall was intended as sarcasm (or, equivalently, was intended as a paraphrasing of the quote above it). So it's probably the latter.
I believe Twic was paraphrasing me, and then asking if my statement (and thus his paraphrase) were satirical.
It was a joke, and I intended it to work regardless of your opinion of Perl and Larry Wall himself. With that said, my own opinion is that Larry Wall did a bunch of interesting things in Perl (some of them merely out of ignorance!), that continue to shape modern languages. The Perl community continues this tradition today experimenting with lots of different programming paradigms, to the benefit of the wider development world.
Most people who design PLs know little about PL design before they do it besides being passionate programmers. Of course, after you design your first PL, you know a lot more.
We are using https://github.com/go-pg/pg for Postgres with some luck, but I think the standard is https://github.com/lib/pq. We are also developing a driver for Orient DB with the goal of it being production ready.
I have been playing a lot with QML lately and I am loving what I see there. Their approach of animations or graphics seems simple and very approachable - http://qmlbook.org/ch05/index.html .
The reason I bring this up is, while I like swift (and own more than one macbook pro), I can't bring myself to code for a platform which ties myself further with Apple eco system.
If you are looking to build a cross platform 2D game for Android/iOS/OSX/Linux - QML seems like a very good choice.
>By now, you may be asking yourself why we would choose to use such nascent languages in a production environment.
No, we realy are not. If anything, both are quite safe bets.
Swift had been in secret development for years and came out almost fully formed (as far as languages go), with a full API, tooling, documentation, etc. Besides it's the Apple blessed way to do things on iOS going forward.
As for Go, everybody and its dog are using it for production, left and right. Including very big names. It's pretty much a given that the language is already production-proven, and will do well in the future.
By "full API" I mean the Cocoa libraries it ties to, not some API for messing with the language (AST stuff etc).
As for developer tools, they have an IDE, a compiler, a debugger, a UI designer, the "code playground" thing, etc. That most of those are (also) embedded in XCode doesn't change much.
Just trying go for some real project, and frankly i wouldn't use it for anything else but network-related middleware ( or low level services), which is what i'm using it for, and almost every tutorial i've read about this language mentions.
As long as you're building something very technical, everything's fine. But once you go out of this path, you can see the walls getting dangerously closer.
For example, i was surprised to see how many runtime errors i got while coding, for a statically typed language.
Surprised as well to see that error checking made me wish for a goto statement again ( what's the pattern for handlings all errors in a block with the same code ? Please tell me..)
Then i started to code against a sql db, and boy did it bring me old memories back. Manual transaction scope handling ( annotation where are thou ?), manually counting bound variables in a statement, finding mismatched types between nullable ones and regular ones....
What a mess...
Now it may only be a matter of waiting for advanced librairies to mature, but somehow i doubt it.
Hi @rohamg, I was wondering if you could please share the libraries and frameworks you are using? I'm also building a mobile app in Swift with a web backend in Go. I've done a bit of research, and I think I'm going to use the following:
80 comments
[ 2.4 ms ] story [ 136 ms ] threadIt lacks things such as immutability and generics. It also perpetuates the mistake of including a run-time type error, null (nil).
Also, I'm not trying to say Go is bad or that I dislike it. It's simply not a modern language as far as design goes.
http://cowlark.com/2009-11-15-go/
So, who's up for an Algol-15 project?
Oh?
Best not to mention that we've also had generics for over a decade, though, wouldn't want to upset him.
I shouldn't have tweaked Java so mercilessly since I did love the language for so long (lo, these many years ago). However, I do think you really underestimate the compile speed of Go. Even if 99% of your builds are incremental, you can't entirely discount the occasional clean build. While it has been many years since I hacked Java, even incremental builds could take more than a few seconds. Even a clean build on our 50 kloc Go codebase clocks in under 2 seconds (0.1s when the disk caches are pre-warmed).
As for generics, I do like them, and seeing them on iOS (with Swift) is a pretty big win. I'm not entirely convinced that Go is much poorer without them, however. The type system in Go is really unique; if you tried to program Go using Java-style paradigms, there's no question that you'd find it pretty disappointing. However, Go does allow some new ways of thinking about types that allow a degree of flexibility that simply can't be expressed with formal class hierarchies.
If what you're doing works for you, just keep on truckin'. But if you give some of these new languages a chance, you might find the areas where they really shine. We did!
You mean like Turbo Pascal 4.0 back in MS-DOS?
Go reminds me a lot of TP with garbage collection, dressed up to look like C to avoid bruising Bell Labs egos. (OK, closures and multiple return values are pretty cool, too, as are associative arrays and array slices)
I'd never bothered with incremental compiling because the build times are so low, but I just tried now and changing 1 java file leads to a 0.3 second rebuild.
For my part, my main beef with Go isn't necessarily the lack of generics, but the obstinate lack of expressiveness. It's back to Java or Python where you're forced to break up your code into discrete, imperative chunks instead of chaining stuff together in elegant flows. It's like Go's authors missed out on functional programming. No pattern maching, which seems like a huge miss considering Go has select { }. Go's syntax is, in many ways, even more rigid than both Java and Python.
That rigidity extends to error handling. While I agree with the philosophy behind Go's rejection of exceptions, I don't agree with how it's been implemented. In discussions about Go people always talk about exceptions vs. explicit error returns, but hardly anyone mentions the fact that error handling completely takes over our code: errs are everywhere!
A concrete example: Go has := for type inference, but it turns out you can almost never use it, because almost every function needs an "err" that you end up declaring. Often you start out like this:
Quite elegant. But then you need to add some more code, and you actually can't rely on type inference anymore: It turns out that just because you needed another "err", you had to rewrite the statement, which is frankly ridiculous.The next problem here is that we can't simply this:
That's because computeStats() takes a Result, not two arguments (Result, error). In functional languages, this is elegantly solved through monads, but not in Go; you can't "short circuit" function chains that might return errors. There goes your expressiveness.Another problem that the error phenomenon infects the language with is that you can't have global variables initialized this way:
To get around this, the regexp module defines an alternative function: (Never mind the fact that you're not allowed to declare this as a const. It is a constant, I want it to be a constant, not a global variable!)The fact that almost every function ends up having an "err" around raises the question: Why is it not an integral part of the language in the first place? Why do I have to declare err?
Other languages (Swift among them) fix this problem through sum types: The function can return a value which is either a real value or an error. Go's idea of returning a value and an error is logically nonsensical in almost every case, because the error is used to signify that the value isn't available due to failure. I'm not a language-theory purist who thinks everyone should really be using Haskell; these are practical concerns.
Overall, Go does feels disturbingly warty in places, which is incredible for a new, clean-slate language. Favourite wart: interface types being magically pointer-based, leading to the whole non-nil value being nil idiocy; it's mindblowing that they got this so wrong.
I liked Go a lot better before I started using it.
The Maybe monad style is so much cleaner, it also allows Elvis operator-like changing, e.g.
foo().getOrElse(blah).getOrElse(baz)
E.g., think of each of those method calls as being a call to some external system (filesystem, memcache, database, etc) that might fail, even if the code's correct. Throwing an exception is usually a pretty bad strategy for those cases if you want to able to sort out what went wrong later.
getOrElse is a function that is used for exactly that, i.e. for things which can fail in the sense that a parser failed to parse some text because there was a syntax error in the string, or the Map datastructure didn't contain the given key so it returns "nil" or "None" or whatever. If you had something that you know wasn't supposed to be able to fail, then you would probably just go ahead and assume that it can't fail, and let the program error out if it does fail since then you've revealed a bug. To use getOrElse for things that shouldn't fail to begin with is not good.
I don't know what you mean by "deal with it explicitly", since getOrElse indeed does deal with it explicitly.
> E.g., think of each of those method calls as being a call to some external system (filesystem, memcache, database, etc) that might fail, even if the code's correct. Throwing an exception is usually a pretty bad strategy for those cases if you want to able to sort out what went wrong later.
getOrElse doesn't throw an exception. It returns "null" or "None" or whatever value is supposed to represent "does not exist" in the case of some failure (failure here being "produced none/nil/null"). It has nothing to do with throwing (or catching) exceptions.
For runtime errors, as I've stated above, I prefer explicit handling close to the error site. I think this is the right tradeoff for server code, though I'm less certain for client code.
For validation errors, I prefer, when possible, to have a parser that either succeeds or fails as a whole, and whose output I can then rely upon to be properly constructed.
For unexpected nils, I do like the option-type/monad approach, though in most cases (e.g., when writing Go, Java, C++, or Javascript) I just let them NPE/segfault.
Note that only one of these cases -- runtime errors -- results in a (result, err) pair in Go. The other two are just about result checking.
I think that the original poster (the one you responded to originally) was talking about errors in the first two senses; things that you should/want to handle yourself. I guess the last thing should be handled with a panic?
> For unexpected nils, I do like the option-type/monad approach, though in most cases (e.g., when writing Go, Java, C++, or Javascript) I just let them NPE/segfault.
It doesn't that you understand idiomatic uses of option-type. Their used for things that legitimately, as a part of the normal operation of the program, can be "null". Not for things that should really not be null. At least I assume that uses of NPE/segfault is not typically used for things that might be null (like returning null from a Map since the value is not there). So, they are not used to "hide" null pointers that shouldn't be null to begin with.
For cases where a nullable type, or Option[T] if you will, really should not be null, it would be more idiomatic to "forcefully" extract the value. In other words, use a function that returns the value, or throw an exception if it really isn't a value (it is null); throwing an exception here would be an indication of a bug. But this use is usually thought of as unidiomatic in languages like Haskell: you should rather make sure that you don't have to "forcefully extract" things like that to begin with.
Right -- I'm only saying that I want to handle the first runtime errors explicitly at the call-site. This is where I explicitly like Go's error style. Other approaches to this problem (e.g., pattern matching) have been discussed elsewhere on this thread, and that's fine for languages that want to go down this route, but the extra language complexity is a tradeoff. I can see coming down on either side of said tradeoff, but it's not cut-and-dried.
> It doesn't that you understand idiomatic uses of option-type [...]
Sorry, that came out wrong. Where the option-type/elvis-operator approach comes up, it seems, is when you need to dig a few levels deep through possibly-nil references, as in cromwellian's .getOrElse(foo).getOrElse(bar) example. I certainly understand how that can be useful, but in my experience it seems to come up most often when dealing with unvalidated inputs (I'm sure there are other cases I'm not thinking of; this is just my personal experience). Whenever possible, I tend to prefer having the inputs parsed, validated, and either accepted or rejected, by a validating parser of some kind. Then I really can just assume it won't segfault as I read through these chained methods/fields.
.. segfault some unknown distance from the place they originated. I mostly do Java, with a substantial dash of JavaScript and Ruby, and in all of those languages, i waste time tracking nulls to their source. A language which blew up as soon as an unacceptable null arose would be huge win.
Actually, having moved from entirely Java to only mostly Java, one thing i've noticed is that in dynamic languages, wrongly-typed objects can propagate as freely as nulls. I spent an unforgivable fraction of today tracking down a bug in a JavaScript app where a framework was passing the wrong type of model to a view. In a strongly-typed language, that would have failed as soon as the framework did that (or perhaps even at compile time), but in JS, it failed in a cryptic way hundreds of statements later.
For extra comedy value, it turned out that the reason the framework was doing that was because i had passed a null into one of its configuration properties - because i'd innocently written something like:
And since the views are defined in files cartView.js and itemView.js, and since the files are loaded in alphabetical order (because we're just throwing them out of Rails and not using RequireJS or similar, i know, i know), at the point at which CartView is defined, Initech.Commerce.ItemView is null!Basically, JavaScript is a language only a Dwarf Fortress fan could love. Whereas i see Go as more suitable for Minecraft fans.
First off, I also find the interface-nil thing to be a frustrating edge-case. I also know that this design was the result of some difficult tradeoffs, as such things often are. We can argue about whether they made the right tradeoff, but I don't think it's fair to refer to it as a "mindblowingly wrong idiocy".
Second, on error handling. Having now written a large amount of server code in Go, I find that I strongly support their approach, even when I find it a bit verbose. Here's how I find it actually plays out in practice:
Eventually, you realize that `return err` just isn't enough most of the time, because it's impossible to make sense out of your logs. So we added a simple "error chaining" function that allows you to pass context when you return the error, which makes the logs much clearer than just a single error message, or raw stack trace. And of course it is often the case that you want to do more logging in your error blocks, even as you ignore the error and move on (e.g., "spurious error reading memcache entry; falling back to the slow thing").The practical reality of writing good server code is that exceptions which get caught way up the stack are inscrutable in your logs (at least that was our experience), so it makes sense to know up-front exactly where failures can happen, and to be forced to think about them the first time you write your code. Yes, it can be a bit verbose, but we've found the tradeoff to be net positive.
But I think it's a good indicator of how parts of Go's design are flawed from the outset. Design is hard to change later, so it's important to get it right from the start.
Again, I totally buy explicit error propagation. I just think Go's solution ends up cluttering your code. It's so focused on errors, yet handling isn't a first-class construct, and the mechanisms it gives you for dealing with errors aren't very good. You have the cast check (a, ok := ...) and you can do a type switch (switch a := err.(type) { ... }), but it's rather weak for something that permeates every corner of the language. At least us have pattern matching.
I find the stack frame problem disappointing. It's amazing that there are now several libraries to create artificial stack frames (eg., https://github.com/facebookgo/stack) just so you get this.
I understand that some may find the error handling a bit too verbose, but humbly submit that there's a legitimate tradeoff to be made in terms of language complexity vs. verbosity, and the Go team generally tends to fall on the side of simplicity over tersity. This works well for us, but not everyone will find it palatable. YMMV and all that.
The stack frame "problem" doesn't seem all that bad to me. It would be nice to have a built-in solution, but our own code for dealing with this is one tiny file someone banged out in a couple of hours, so I'd hardly call it more than a stumbling block. After watching the Java world deal indefinitely with untold design flaws in core libraries, I support keeping things simple at the outset wherever possible.
But hey, there are lots of choices out there, so I'm not telling anyone they must write their servers in Go. Just that it works well for us.
It's all so cute being a startup who hasn't released anything. All of that operational nonsense like remote debugging/monitoring, log management, hot reloading etc just isn't an issue (yet).
It's not really my place to argue for or against them, just figured I'd share an anecdote.
:-)
To ask developers to go through that again is a bit rich.
Being able to survive something is a pretty low bar.
"What it says is that he finds writing containers like lists of ints and maps of strings an unbearable burden. I find that an odd claim. I spend very little of my programming time struggling with those issues, even in languages without generic types."
and I had literally not three days before fixed a bug in somebody's c++ code caused by using their own hand-rolled stack class instead of std::stack, so I'm not inclied to be charitable on go's lack of generics.
Go looks like it was invented in the 80s.
Go isn't yet faster than Java, but it is closing the gap, at least on multi-core: http://benchmarksgame.alioth.debian.org/u32q/benchmark.php?t...
Single core doesn't fare so well (Go vs Java), though: http://benchmarksgame.alioth.debian.org/u32/benchmark.php?te...
Perhaps the authors should have used Rust, or Ada, rather than Go? Hell, on a single CPU machine, FreePascal seems to do better than Go :-)
Actually, from a "performance, w/out turning off all the safeties" standpoint, it really looks like Rust is owning that space.
Now I gotta learn Rust... :-)
http://blog.golang.org/4years - From Nov 2013
No, the Go optimizing isn't great. This will be addressed after the compiler is completely rewritten from C to Go. The Go community is growing too, which is probably more important than how well you do on a micro benchmark.
http://talks.golang.org/2014/c2go.slide#18
And, the gccgo compiler does leverage gcc, assuming that counts as a mature compiler:
http://blog.golang.org/gccgo-in-gcc-471
. Neither of these contradict your implicit point of "they chose to invest time in this port instead of trying to maximize performance ASAP", but you still might want to know.
It seems you'd lose out on picking up improvements from the main compiler branch and have to keep transpiling and gardening in patches. Plus upstreaming improvements would also be irritating.
I'd think a Go frontend to LLVM would be optimal in terms of productivity.
https://blog.golang.org/gccgo-in-gcc-471
The Go developers have claimed LLVM was not useful because it's too slow, which is why they used the plan9 compiler style.
Here's java compared to go on all benchmarks, 64-bit:
quad-core:
http://benchmarksgame.alioth.debian.org/u64q/benchmark.php?t...
single-core:
http://benchmarksgame.alioth.debian.org/u64/benchmark.php?te...
Gives a much better overview than a single test running under 32-bit .
I thought that single vs multi-core was comparable on either 32 or 64 bit.
Otherwise, I thought I was pretty clear why I picked binary tree over, say, mandelbrot set. Wanted to favor "data structure" usage over number crunching.
More addressable memory is but one advantage of 64-bit versus 32-bit, the fact that it has double the amount of registers is another VERY important performance aspect since registers are by far the fastest place to store and retrieve data from, so less need to push values from registers on to stack / flushing cache lines, means faster running code, and in hot loops the difference in performance can be huge.
>Otherwise, I thought I was pretty clear why I picked binary tree over, say, mandelbrot set.
But why not show ALL the results and just highlight binary trees, there are a lot more benchmarks there than mandelbrot, also I don't see what is wrong with 'number crunching' benchmarks ?
Most business apps don't do much number crunching or bit-twiddling. Think of moving strings/records/objects to and from a data store, putting them temporarily in an in-memory hierarchy or an indexed cache for lookup/evaluation, then pumping more strings to a browser / the console / a dialog box.
I didn't pick the binary tree example to pick on Go, it's my "best match use case" for the "allocate and find things" that I envision goes on in most applications, regardless of which languages I am comparing.
I love the concept of rust, but trying to write a trivial program in it was a pain in the ass.
The reason things have been changing so much is mainly to get a better language now, and not have to write a 2.0 that changes everything. When you look at rust code from six months ago, things have improved greatly, so it will hopefully be worth it.
Larry Wall is the ne plus ultra of programming language design. This is satire, right? Please tell me this is satire.
Or maybe it's your comment I'm misunderstanding?
Edit: Ah. It's been pointed out that perhaps your own remark about Larry Wall was intended as sarcasm (or, equivalently, was intended as a paraphrasing of the quote above it). So it's probably the latter.
It was a joke, and I intended it to work regardless of your opinion of Perl and Larry Wall himself. With that said, my own opinion is that Larry Wall did a bunch of interesting things in Perl (some of them merely out of ignorance!), that continue to shape modern languages. The Perl community continues this tradition today experimenting with lots of different programming paradigms, to the benefit of the wider development world.
The reason I bring this up is, while I like swift (and own more than one macbook pro), I can't bring myself to code for a platform which ties myself further with Apple eco system.
If you are looking to build a cross platform 2D game for Android/iOS/OSX/Linux - QML seems like a very good choice.
No, we realy are not. If anything, both are quite safe bets.
Swift had been in secret development for years and came out almost fully formed (as far as languages go), with a full API, tooling, documentation, etc. Besides it's the Apple blessed way to do things on iOS going forward.
As for Go, everybody and its dog are using it for production, left and right. Including very big names. It's pretty much a given that the language is already production-proven, and will do well in the future.
Full API? I believe apart from xcode, there are no developer tools or no examples of parser API. Am I missing something?
As for developer tools, they have an IDE, a compiler, a debugger, a UI designer, the "code playground" thing, etc. That most of those are (also) embedded in XCode doesn't change much.
As long as you're building something very technical, everything's fine. But once you go out of this path, you can see the walls getting dangerously closer. For example, i was surprised to see how many runtime errors i got while coding, for a statically typed language. Surprised as well to see that error checking made me wish for a goto statement again ( what's the pattern for handlings all errors in a block with the same code ? Please tell me..) Then i started to code against a sql db, and boy did it bring me old memories back. Manual transaction scope handling ( annotation where are thou ?), manually counting bound variables in a statement, finding mismatched types between nullable ones and regular ones.... What a mess...
Now it may only be a matter of waiting for advanced librairies to mature, but somehow i doubt it.
There are also type annotations (tags), though I don't know how/if they relate to sql
iOS:
* MagicalRecord - CoreData abstraction (https://github.com/magicalpanda/MagicalRecord) * mogenerator - better CoreData class management (https://github.com/rentzsch/mogenerator) * RestKit - with REST API endpoint (http://restkit.org/)
Go:
* Gin - Web framework. I really like the focus on performance, and that it seems to include all the features you need for an API (authorization, route grouping, etc.) (https://gin-gonic.github.io/gin/) * gorm - for most CRUD operations / queries (https://github.com/jinzhu/gorm) * sqlx - for more complex queries (https://github.com/jmoiron/sqlx) * apns - push notifications (https://github.com/anachronistic/apns) * gingorelic - New Relic integration (https://github.com/q1t/gingorelic) * APIBlueprint - API documentation (http://apiblueprint.org/)