Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

This roughly lines up with my feelings. Go is a solid improvement over many languages that we inherited from the 70s, 80s and 90s.

But it also retains a certain "we don't need a robust type system; weak-ish static typing is good enough" ethos that made sense in back then, when compilers were hard enough to write that it was easier to justify making the programmer handle more things manually for the sake of simplifying the compiler authors' job.

This is always a tradeoff, of course, but I think that the optimum balance has shifted even further in favor of programmers. Some of Go's decisions still made sense in the 2000s when the language was first being created. But now, 15 or so years later, I think many of us could be forgiven for wishing for a language that's a lot like Go except that it dared to dream just a little bit bigger.



Language design is always easier in retrospect. It is harder than it looks, and often harder than the people designing a language realize before it is too late.

I think it was a good choice to take smaller steps and try to not be too ambitious too soon. Sure, Go isn't the most sexy language from an academic point of view, but it has a certain conservative and pragmatic approach that does work. It does generally result in code that is a lot easier to read and maintain than is my experience with C, C++, Java, C# and a few other languages I've worked in.

Go is an engineering language - not an academic exercise.

Take, for instance, the approach to generics. They could have designed that in from the beginning, but they showed restraint and didn't. That probably took a fair bit of courage. It is my impression that they hadn't figured out what generics should look like in Go, so they postponed until they had a better feel for how it ought to be done. Rather than risk making choices that would be hard (impossible?) to rectify later.

When you add something to a language there is always the risk that you make it worse.

(I'm not making any qualitative judgements on Go generics since, frankly, I don't feel qualified. I make very sparing use of it because it really isn't that often I actually need to make use of it)

People tend to forget that Java didn't have generics until 1.5 (or 5.0 or however they prefer to version it now) - about 9 years after first being launched. And to be frank, that was not a fun experience at all. Not so much because there was something wrong with the design, but because suddenly a lot of people went overboard and started designing really hairy types that could be hard to figure out and use.

If you consider C++: C++ spent 20+ years flailing wildly and the result was that you got lots of different C++ "traditions", subsets and practices. Sometimes within the same company. Depending on which era or tradition a C++ codebase is from you may have to adjust to a wholly different way of programming from what you are used to or prefer. And as for generic programming: in what world is STL a neat solution? And to this day, compilers are slow, they produce rubbish error messages, the toolchain still feels like a 1970s ad-hoc mess, and there is no definitive way to build things, resulting in lots and lots of additional complexity when trying to tame the horrific tool chain.

Sure, they could have put loads of stuff in Go from the beginning. But I think they would have gotten a lot more wrong if they had. I really appreciate that they are evolving the language slowly and conservatively.


> Go is an engineering language - not an academic exercise.

Great summary. I personally think Scala is the antithesis to Go, it being an academic exercise - with complexity and tooling to boot.

re: Java generics, I've got this comment from 2015 bookmarked, it's a great explainer about the decision process behind adding generics to Go and the problem(s) with Java's implementation: https://news.ycombinator.com/item?id=9622417


Thanks for posting that link. It lead to a lot of interesting background material! I think this quote in particular resonates:

  > those ideas simply haven’t had time to pass through the filter of 
  > practical experience


I remain somewhat unconvinced on that front. There were good implementations from outside the functional programming sphere (which seems to be what people from the Go space tend to mean when they say "academic") already existing at the time. I think the real problem is that the most well-known examples, C++ STL and Java 5 generics, were trying to stick generics into an existing language with a "some things are objects and some things are not objects" type system. My take on the story there is that generics called attention to some deep deficiencies in that kind of type system, and people responded by shooting the messenger.

Languages like C# (which I've used extensively) and Eiffel (haven't used in anger), on the other hand, have a consistent type system, and didn't seem to have the same problems with generics that Java and C++ did. Or for a non-object-oriented model that has some other features that might be attractive to Go, such as not allowing implicit specialization, there's Ada.


I think the key question is how robust a type system can be while keeping compilation extremely fast. Slow compilation absolutely destroys developer productivity.


You can make it pretty darn robust! OCaml compiles very quickly and has a good type system. Rust is slow due to LLVM, macros, and the compilation unit being the entire crate. None of these are requirements for a good type system


Zig's new compiler is a nice example of how you can do all sorts of inherently slow things at compile time and still have a lightning fast iteration time for developers if you design your toolchain for incremental compilation instead of making the compilation unit be the entire module the way Rust did.


I'm wondering if there will be a language that compiles to Go, like Typescript is for Javascript, that adds a stricter type system (and fixes other issues) and adds a compiler step, but still has all the other benefits of Go.


Conversely I could go longer between compiles if the type system caught more.


Most of the times when I really care about quick feedback cycles is when I'm experimenting to see what approach does and doesn't work well in the first place.

I care a lot less about it for "simple" errors like typing errors, typos, or whatnot. This is mostly trivial stuff compared to "is this entire approach good or not?"


I think I agree with you, but in trivial projects even the infamous slow compilers are fine. Toy Rust projects and small Java projects get compiled very fast.


It doesn't even have to affect compile times per se, it could be a linter/checker task running separately from the main compiler; most type errors are only relevant during development in my experience. Said experience lately is a lot of Typescript, where all type information is no longer relevant at runtime because it's compiled out or just bluntly removed to make it valid JS (I dunno how the TS compiler works).


Yep this is one of the reasons lots of my friends switched to Go: short compilation time (like good old Pascal days).

Rust & Haskell is a big no :p


But we're not talking about replacing Go with something like Rust or Haskell here. We're talking about really basic, inexpensive things like not repeating Tony Hoare's billion dollar mistake yet again. Or actual structured error handling instead of something that's ergonomically quite similar to classic C-style error handling aside from the relatively incremental improvement of not relying on global variables.


> not repeating Tony Hoare's billion dollar mistake yet again

This is the killer for me. After using Swift for a while, I no longer have the tolerance for languages that don’t have proper option types (and perhaps more importantly, which don’t use option types pervasively and by-default.) It’s just 100% the wrong way to design a language nowadays. Option types are table stakes.


It doesn't even have to be ML-style option types. Kotlin is a great example of an alternative implementation that arguably has much better ergonomics for procedural and object-oriented code.


I'm not so sure that we're necessarily talking about small, inexpensive changes here. Anything that changes the fundamental character of a language is probably going to have bigger consequences than you might imagine.

I've been writing a fair bit of C lately. I'd say that the way Go error handling works has exactly nothing in common with C. Despite spending 15 years mostly writing C on UNIX 20 years ago, when returning to C it was striking how little Go's convention of returning multiple values, with the error being the last, has in common with C.

And if by structured error handling you mean things like exceptions: I don't really see how that improves error handling from a readability or security point of view. In Java people can't even agree on whether or not to use checked exceptions (ie neuter any perceived advantage of exceptions), and syntactically, exceptions are a little bit more awkward than return values as you invariably create nested scopes and move the handling of exceptions away from the normal program flow - which is not a readability win.

But I'll grant you that the error type in Go was both a bit vague and there was a lack of sensible stuff to, for instance, wrap and accumulate errors initially. But that has gotten better.


Ergonomically, the thing that Go and C have in common about error handling is the thing that the grandparent pointed out - you don't get much language-level support in helping you to remember to check for errors. If you forget, then there's a decent chance that the error condition will be allowed to silently propagate.

Structured error handling is as opposed to exceptions. I think that the most famous implementation these days is Rust, but if you want to see a version that isn't built on top of an ML-flavored type system, check out Zig's implementation of the concept: https://ziglang.org/documentation/master/#Errors


Error handling is tricky because it is (usually) people who write code, and people are lazy and sloppy. Force people to deal with errors and they'll find new ways to not really handle errors.

I'm not opposed to language support for pushing developers to handle errors. But experience suggests it isn't the magic bullet we sometimes tend to think it is.


Completely agreed.

I'm only responding to point out that I never claimed or even implied that there's such a thing as an error handling magic bullet, just that I think that Go's approach to error handling has been oversold. It compares favorably with an "enterprisey" approach to exceptions, precisely because that method makes it so easy to hide the dead bodies. But I don't love that Go's (and C's) approach makes it so easy to implicitly suppress errors, because that's effectively the default behavior. In a language that forces you to check for errors, by contrast, it's harder to be lazy and sloppy without being explicitly lazy and sloppy, which then means that such behavior is more likely (not guaranteed - again, I'm not trying to claim a panacea - just more likely) to be noticed during code review.


Additionally, codegen and templated metaprogramming is very fast in go. Gen + Compile + Lint + Test on Save with line-by-line coverage overlay in <10s.


I’ve used a number of languages over the years and I have never been more productive than with Kotlin.


> 70s, 80s and 90s

It reminded me of Go vs. Algol-69 http://cowlark.com/2009-11-15-go/




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: