> That said, for some projects I’d prefer Rust. Specifically for very large projects, a heavier reliance on abstractions starts to make more sense.
This is an interesting point, because I've never actually worked on a large project that shipped. Lots of brow furrowing and hand wringing over correctness, but at the end of the day nothing for customers to use, or too late for it to matter - which is more or less the same thing.
Conversely, I've seen plenty of small "shim" projects that grew, sometimes rapidly, into much larger projects. Few, or no tests. Awkward, meandering blocks of logic, often with developers lamenting the lack of good abstractions (myself included).
Large projects tend to be second systems, and second systems tend to suffer second system effects.
It seems as though the profession of software development curses its practitioners to remain unsatisfiable.
I've worked on many (if not all) parts of systems that are over 1,000,000 SLOC written in languages like Java and C++. I always felt that if I'd used a more powerful language those systems would be expressible at a 1/5 to 1/10 the SLOC.
Once I discovered Lisp, I found that more powerful language. In my experience (I'm now a full time Golang developer) for the things that Go does well, Clojure creates much smaller amounts of code and performs at least as well. When I program in Go, I revisit my younger self again feeling the same frustrations. I don't see Go as The Way Forward except for small "infrastructure" programs in the M2M space.
I've never been a Go evangelist. For years, the appeal and praise lavished upon Go flummoxed me. Until I decided to use Go for my latest product. Then it made sense. For most purposes Go is "good enough." And it's good enough in enough areas for me to set Go as the standard, de facto language at my company, unless there's a damn good reason to use another language.
I appreciate Go because it's fast, small (re: syntax and standard library), garbage collected with nice primitives (pointers and slices), concurrent with high-level primitives (goroutines, channels, select), expressive enough (strings, maps, range, first-class functions, type system, etc.), first-class support for Unicode, and it has a rock-solid standard library.
About a month ago, I started learning Go. It's so small. The language, the standard library, the tools. They're all so easily digestible; it took me 4 days to work through the Go playground, read the entire Go specification, work through and learn 40% of the standard library, and start writing production-ready programs. C was the last language that enjoyed a language/standard library this small, to me.
The same thing can't be said for Ruby or PHP, or any other languages I've worked with. In almost every project I've been involved with, there would be usage of some arcane corner of the language. Whether it's determining the byte offset of a member function in C++, or the GCC extensions to structure initialization in C, or metaprogramming magic in Ruby. All these cracks and crevices are difficult to keep in your head. This isn't so with Go. It's easy to keep the entire language and standard library in your head; providing a certain ease and flow when building a program.
Ease of use applies to memory management as well. Go offers just enough control over memory to make it pleasant to implement and use real data structures. Try implementing an LRU cache in Ruby/Python that evicts based upon an object-size policy without wasting a ton of memory simply maintaining a linked list. When I built a distributed real-time, type-ahead service in Ruby I was fighting the language the whole way when it came to simple data structures, memory management, and concurrency. (It was just a prototype, I know Ruby wasn't the right tool.) Originally, I was going to rebuild the service in C++, but that would've taken too much time. Instead, I opted for Go and it was much easier, and obviously much more performant. Not as performant as an equivalent C++ service, but performant enough.
I don't think much needs to be said about Go's concurrency. First-class primitives such as goroutines, channels, and selects along with solid standard library support in sync and sync/atomic, makes for a powerful combination. Go wraps these concepts in a very bland, mainstream way. Making them more accessible to more people with little time investment.
You're right, Go will not win based upon the # of LOC written. However, after writing 20k+ LOC in Go over the past month, I don't see any reason to write any of our back-end code (infrastructure, services, web servers, tcp servers, etc.) in anything other than Go. It's good enough in enough areas to make it much faster for a team to standardize on just this one language. Not only does this reduce the time wasted due to context switching between languages in a polyglot shop, it makes it a ton easier for anyone to jump in and contribute anywhere at anytime.
---
To give my thoughts some context, here's a subset of my professional and personal language/project history:
- Ruby: 100k+ LOC Rails monstrosities and smaller 20k LOC projects
- PHP: 200k+ LOC bare-PHP e-commerce websites and 50k LOC CodeIgniter/Laravel intranet apps
- Python: 10-20k LOC utility and server management scripts
- C++: 500k+ LOC open-source 3D game engine and multiple <10k LOC personal projects
- JavaScript: ~35k+ LOC for the front-end work on some websites I've worked on
- Common Lisp: read all of Practical Common Lisp and ANSI Common Lisp, and dabbled in a few small programs
Well I'm not surprised you can't see any reason to write anything other than Go when you've never used a language with a decent type system! If you're ever standardizing the language for a whole team I hope you'll take the time to evaluate a decently typed language first - OCaml or Haskell or F# or Scala (or I guess Rust if you absolutely need to avoid GC)
I wish I had the time to evaluate everything that is out there but I don't. There's simply more important things to focus on for my company than choice of language: sales, hiring, fundraising, etc. Therefore, I had to choose the best option for me with the limited information I had at the time. But that doesn't mean I can't be convinced to adopt another language. :) If someone ever surfaces a highly persuasive point-by-point argument for OCaml or Haskell or F# or Scala that applies to what we're doing, then I'll reconsider. So far, I haven't seen such an argument.
You should of course use what language works for you. That said, the series on moving 0install from python to ocaml might be the kind of poin-by-point argument you're after?:
For me a big thing is avoiding error prone inconsistencies and other footguns. The less footguns a language has, the better.
For example in objective-c, most of the language deals with nil items without crashing or errors, except for a few APIs and collections. This abstraction mismatch in collections causes runtime crashes and is a language footgun.
Swift fixes this footgun with optional types, but objective-c could of made it's collections nil-safe and apple could of just declared in the company that your apis have to be able to handle nil arguments, no exceptions. The new nullable annotations are an ugly patch to just help transition to swift and still do not fix the footgun.
Java's NPE is another example of a footgun, and C++ & C have footguns everywhere that generate billions of dollars of security industry work.
What would you call the footguns of lisp & golang?
Right now, Denzel's points are spot on. I would add Go the compiler itself is something of a footgun. It routinely compiles things it shouldn't. Go vet is the bandaid on this.
Common Lisp's biggest footgun is probably its lack of opinion about anything. It's truly multiparadigm and probably not in a good way. Alan Kay: "Lisp is not a language, it's a building material." A real world example: I had beers with the SIFT guys (a local AI consultancy here in Minneapolis) a couple of years ago. They explained to me that when they have a problem, they go and type text in an editor that explains the problem formally. Then they type words in that problem's "language" that allows them to express a solution. Then they go an implement that language they just came up with using Common Lisp.
Clojure on the other hand is a Lisp with an opinion: parallelism can be done in the same idiom as non-concurrent logic if you use immutable constructs. So the language will push you in this direction. It turns out writing code without branches, variables, objects and yes even state can be done 99% of the time this way. And yes it is simpler in the truest sense of the word.
Off the top of my head, for Go, (1) the semantics around nil and closed channels resulting in deadlocks, and (2) failing to recover from an unexpected panic. I think error handling and documentation of the errors returned by methods could be improved in Go. I dislike having to comb through the Go standard library source to see what types of errors a method returns.
I can't speak for lisp because I don't have enough experience with it.
Fair enough. I'm reminded of Hickey's talk on the difference between "simple" and "easy". I just did a CLOC on the latest golang github repo: its now at 1,000,000 LOC. The Clojure repo (which is considerably older)? 40,000. Talk to me in a few years when you tire of this.
Isn't the runtime implemented in Go since 1.5? A quick overview of the C content[0] shows that the C source in Go is mostly cgo (either test cases or the runtime integration[0]) and the shootout C sources (for bench tests?).
You started learning Go a month ago and, as a total beginner, you've written 1,000 lines of code per day in it? (20000 / (4 weeks * 5 days)). 125 lines per hour! This is a somewhat unbelievable figure.
I'm also skeptical about coming to decisions like this:
> And it's good enough in enough areas for me to set Go as the standard, de facto language at my company, unless there's a damn good reason to use another language.
...based on a single, one-month, one-man project. That's like deciding to only use a hammer to build everything from now on just because the hammer worked so great with a birdhouse.
I work 14 hours, workout for 2 hours, and sleep 6 hours a day, 7 days a week. This is my company and I'm bringing it into existence. Not to mention that LOC count includes testing which I write in standard TDD fashion. Furthermore, a number of prototypes were built in Ruby prior to working with Go. And the design for the core set of services was thoroughly planned and documented.
While I may be a total beginner in Go, I am absolutely not a junior software engineer. Go lends itself to being learned quickly.
No. It's not based on a single, one-month, one-man project. It's based upon a series of pain points I've felt over the years working in a number of different languages, on a number of different projects, with a number of different teams. Go solved enough of them to be worthwhile.
Your simile is flawed. It's like deciding to use one hammer to hammer all nails from now on until it breaks. I'm optimizing for aggregate throughput of a mid-sized team working on the problems we have over the course of 3 years. Go fits the bill for all the reasons I discussed above.
If you want to offer up a more detailed point-of-view, please do. That'd be much more appreciated than such a vapid, dismissive piece of criticism.
This is an interesting point, because I've never actually worked on a large project that shipped. Lots of brow furrowing and hand wringing over correctness, but at the end of the day nothing for customers to use, or too late for it to matter - which is more or less the same thing.
Conversely, I've seen plenty of small "shim" projects that grew, sometimes rapidly, into much larger projects. Few, or no tests. Awkward, meandering blocks of logic, often with developers lamenting the lack of good abstractions (myself included).
Large projects tend to be second systems, and second systems tend to suffer second system effects.
It seems as though the profession of software development curses its practitioners to remain unsatisfiable.