> ...[rant about 'inheritance' and 'subclassing' and 'hierarchies']...
> If C++ and Java are about type hierarchies and the taxonomy of types, Go is about composition.
How do you get all the way through designing a programming language without realizing how incredibly wrong-headed this is? "Inheritance hierarchies are bad", so... you exclude generics (which are compositional, and have nothing to do with inheritance), and only support subtyping polymorphism via 'interfaces' (ie. inheritance)? That is the exact opposite of his claimed principles!
(And then as a minor concession to programming usability, he adds generics anyway, but only for built in types (maps and arrays), so advanced data structures are still an exercise in void* wrangling.)
But that has nothing to do with having an expressive type system and generics.
We can look at Rust for an example. It's "OO model", such as it is, is fairly similar to Go's. You can define a thing that combines data and behavior. You can define traits/interfaces that objects can implement. You can use those trait/interface types in place of concrete types. But Rust also gives you generics, sum types, and pattern matching. IMO, that makes for much cleaner code than doing the equivalent in Go.
«Rob Pike doesn’t know what type theory is or why it is useful»
Look at his latest statement on «generics» in Go: https://evrone.com/rob-pike-interview
He acknowledges that parametric polymorphism is a good thing that Go is gonna get, but then repeats his ignorant view on types by conflating type theory with shitty OOP inheritance and class hierarchies.
there was no need to rush an implementation of generics.
Then one day I had to implement the swap operation for sorting. Again. And I thought that even C's qsort was better, and WTF am I wasting my time on this half-assed language. I dumped it, tried rust, and even with its slow compile times I couldn't be happier.
Now a few releases later, they fixed sort so I can only implement compare. Sorry, it's not nearly enough. Essential parts are simply missing. Exhibit A is source code generation, a clear indication go isn't enough on its own. I'm not working with an ecosystem where my human time is less important than the language philosophy. I want to express a repetitive pattern in the language, and then never think about dumb bureaucratics again.
It was a near miss, though. SOme things are clearly correct. I want to look again when they finally have generics, and some basic collections. I hope that day comes and I can give it a new chance. But until then, less was simply not enough .
This isn't Go fanboyism; I've worked with lots of languages in my career, and I still get really excited and try new languages all the time, but they all tend to optimize for nice-to-have things (such as a type system that can handle those 1-5% of use cases where Go would have you drop to interface{}) in exchange for essential things like sane tooling. Rust stands alone as a possible exception, but only since it shipped non-lexical lifetimes, rust-analyzer, and a long tail of other improvements that have recently brought its usability down into a range that is acceptable for general application development.
Not being able to package build and runtime dependencies with proper versioning is an incredible pain.
Ironically, the fact go tried to make me use GitHub as a stopgap package manager (& denied one was necessary) while they built their own was what made me run away screaming.
They've since fixed that but seeing them fail at what Perl managed in 1995 and what became standard is larger languages around the turn of the millenium in a brand new language in 2010 wasn't a great omen.
Java and C# are getting native compilation soon (GraalVM, CoreRT), and when they do, it's hard pressing to see why anyone would settle for an inferior language (golang) over mature and better designed systems.
Writing code is not where time is spent. Repeating oneself, duplicating code, is fast and easy. Debugging someones code that felt like "expressing" themselves through it however, takes time, and a lot of it.
I really have no idea why some people so often need to use generics, and similar, beyond the built-in map and slice/array, when I seemingly do not, even though I write Go as my full-time job. Sure there a lot of difference between apps/domains, but the bulk of the code is usually not that different.
But more code written equals more code to read. Adding a field to a struct means modifying all the boilerplate too. It is all to common to forget just one place. Boilerplate plus maintenance attracts bugs.
Generics are easily overused and abused, but a well placed generic can save a ton of time.
As an example, and the pnly generic I wrote in a week, today I had to generate a ton of test data programatically in java. So I wrote roughly a 3 line method
T select(int index, T... values)
and used it on each of the fields with different types like this: struct.anInt=select(i,0,1,INT_MAX);
This allowed me to quickly iterate trough all combinations of common and edge cases. If I had to specify all cases individually, there was no chance I'd exhaustively test every combinations.They're also impossible to implement and extend without generics.
If your mental model of writing a program is giving instructions to an abstract machine, generics may seem pointless. However if you're interested in representing a problem and its solution, and allowing the language tooling to translate that into an optimal list of instructions for an abstract machine - generics are pretty critical.
Things that in other languages I can do in half a dozen lines of code, I find I'm writing 4x time more in go.
And I find it pretty unreadable, it's not nearly as expressive as other languages I enjoy using.
There's too many missing features I use heavily in other languages that make my life easier that I really miss them in go.
I will say I do like that its opinoinated on the formatting. Just takes away an entire tiresome argument.
I tend to conflate higher expressiveness with being "clever" until you're really proficient in the language. I think Go's main value prop is that it performs well with very little ramp up time compared to languages with more powerful features.
If you have a team of people who are really strong with something like OCaml or Scala, they'll be amazingly productive. But if you have a rotating team of engineers with varying backgrounds, it's hard to beat Go when you're talking about time-to-productivity.
Want to reverse an array? You can't just list.reverse() or list[::-1] like you'd do in Python. Want a simple lookup if a value exists? You can't ['a', 'b', 'c'].include? 'a' like you'd do in Ruby. Want to reach for low-level primitives? You'd have to delve into CGO territory, which is not always "elegant". Want different concurrent paradigms (eg. an actor model), well, maybe you're better off without it.
But it's up to you whether that kind of 'simplicity' is desirable or not.
Testing for membership in an array is O(n) in almost any language - and there's a lot of new coders who will happily call it inside a loop. My second internship, I reworked an O(n^4) method to O(n^2) for some code written in R by a senior statistician. This raised the feasible N from 4 to about 13 (in terms of what results you could get in two or three days of compute).
Google doesn't want anyone making that mistake in production, where throwing more servers at the problem costs a whole lot of money. And they hire a lot of junior devs.
I'm sure as somebody who's writing their own raytracer, you know the performance cost of this lookup intimately, and you've made the performance trade-off when picking your data structures. (E.g: maybe the include function is called rarely, or it really is just 3 items, etc). But Go is made for teams of varying skill levels, and so it takes the position that doing uncommon things should take some extra work.
Yes, they were introducing even more complexity to a complex language, but how else are they supposed to incorporate better ways of doing things while maintaining backwards compatibility?
I suppose one could always just build a new language that doesn't have the historical cruft, but in this case that's Rust and not Go.
Whoa, hold it there... inheritance and subclassing are OOP, types is a different subject, right?
I wish this weren't true. It's quite damning.
It's less bad in a real-time conversation, with fast feedback about how well we're being understood. But it still hints that we don't have the concepts clear in our own minds.
From golang documentation:
"Although Go has types and methods and allows an object-oriented style of programming, there is no type hierarchy. The concept of “interface” in Go provides a different approach that we believe is easy to use and in some ways more general. There are also ways to embed types in other types to provide something analogous—but not identical—to subclassing. Moreover, methods in Go are more general than in C++ or Java: they can be defined for any sort of data, even built-in types such as plain, “unboxed” integers. They are not restricted to structs (classes).
Also, the lack of a type hierarchy makes “objects” in Go feel much more lightweight than in languages such as C++ or Java."
After this and a few other issues I had with Go's design, I just started learning Rust. Though I hear good things about Swift these days, hopefully they will provide full support for all popular platforms in the future.
* C++, easily doable with templates.
* Python, easily doable with duck typing.
* Rust, doable, though with some restrictions. I needed to require the derivative function to return the same type as its input, rather than returning something that can be scalar multiplied and added to the same type as the input.
* Java, not possible. Adding/multiplying of built-in types can be done with + and * , but user-defined types require .add() and .multiply()
* Go, not possible. Go doesn't support generics, and I'm certainly not going to copy/paste a numeric method once for every system that I examine. Also, same issue as Java with no operator overloading.
Complexity has to go somewhere. By aiming for a simple language, Go forces the complexity to be in the developer side instead.
I find Go too limiting and definitely not offering "so much more". The author is probably right mentioning "big teams". Sure if I have whole shebang of programmers each nibbling at small particular task and appropriate budget it will work. But comparing the amount of work per developer per time I can have with more sophisticated "traditional" languages does not put Go in any good standing in my opinion.
I really like go, and I daresay it makes many things easy to implement and understand, but I do find the flexibility/lack of sophistication allows one to end up writing what amounts to improved C. Great for smaller projects, but surprisingly difficult to manage as it grows.
And in what ways was it better? Power and simplicity (at least for writing those kinds of programs).
Developers are constantly looking for new languages to fiddle with without any objective reasoning.
Second, you seem to have a strange definition of "success". Limbo was a success? Well, some people used it, and some software got written in it, and some people used the software. Not much software and not many people, though, in the grand scheme of things. Same with Oberon-2. Even if you consider those two languages to have been successes, Go is a far greater success - it's successful in a way that neither Limbo nor Oberon-2 ever were.
* I guess many people will disagree for the lack of generics (among other things), but the compiler and static types are powerful tools.
For example, in Java one can trivially create a class like `Maybe<T>` with a method `Maybe<R> map(Function<T, R> mapper)`. This can be used with any classes filling in the parameters. I believe golang is unable to express this.
https://github.com/protocolbuffers/protobuf-go/blob/master/i...
Specifically the last one of embedding an empty array of mutexes into a struct prevents that struct from being copied. This is something that is easily and explicitly accomplished in C++ by making the constructors private.
I also adore goroutines and miss them in any other language. They are really fibers and let you do light concurrency without fiddling with async constructions. Async programming is just an ad hoc way of implementing fibers, and it imposes more cognitive load because now you have two different approaches to every problem in the same language (one async, one not).
IMHO minimizing language-imposed cognitive load should be a core goal of any programming language. Human brains are finite, and programmers should spend the majority of their mental energy thinking about the problem being solved not the language.
I agree, goroutines (or perhaps something a little more generic that doesn't conflate stack reification with scheduling) are absolutely the correct abstraction, and this will be proven in a few decades time. But until then we're stuck with people rationalizing the limitations of async constructs in their favorite languages; constructs primarily chosen because of quirky design constraints, principally C interop and C-based VM implementations that leak the semantics of the C ABI stack.
I have immensely enjoyed the "less is exponentially more" philosophy with Go. In Go there is one or a small number of way to do something. Contrast that with C++ where there are any number of ways to do something and at least as many styles of coding. Too much choice results in complexity.
IMO the generics debate is overblown. The systems I've build with go tend toward heavy data processing (ETL, correlation, extraction and the like) and service (JSON APIs supporting front-ends; event some front ends with Go Templates). Never once was I like "my life sucks because I don't have generics." Fair and balanced: Processing JSON in Go takes some getting used to, especially when the JSON structure changes. However, there are some good libs to help. It's just a friction point given the typed nature of Go.
The operational simplicity of Go cannot be overstated. Go's production of a deployable binary with zero dependencies is a blessing. There's an amazing deploy tool you can use called: cp. Compare that to the library version hell that is C++ (and I'm also a C fan and very experienced dev). No. Thank. You.
The cross compilation of Go code works beautifully. Just 2 weeks ago I had to demonstrate a tool to a customer and due to COVID it was virtual. We had problems with MS Teams connecting to our dev platform so literally 15 minutes before the demo I cross-compiled to a Win binary and moved it to the machine hosting the Teams call. Victory.
Because of go fmt, all Go code looks the same. That's a massive advantage to read/understand/use OPC (other people's code). Ditto for the automatic documentation generation -- it looks the same and behaves the same across Go projects.
I abhor the Java ecosystem for so many reasons I just don't have the energy to go into. It's my opinion, so not up for debate. If Java works for you, by all means have at it. Robust solid technology for sure. Not hating here; just choosing.
So, my Go journey has been and continues to be fantastic. It is my preferred platform for pragmatic reasons--the same sort of reasons it was developed by Google in the first place. My team and I can get stuff done quickly. We can scale it. We can leverage all the CPUs on a box running big data processing flows. We can have automatic code-linked documentation and formatted code and can leverage the same from others. We have build in testing. We can make high performance services and servers with ease. We don't have to deal with ridiculous configuration nonsense. We get instant compilations. We get great tool support (vscode + Go for the win). We get a batteries very much included stdlib.
And most of all we get a platform that understands and managed complexity in a favorable way.
$0.02. Ok, maybe $0.05 (inflation).
If you can gather some energy, I'd be interested in that.
-- Compile times!
-- Operational complexity vs. Go.
-- Built-in tooling vs. Go.
-- Verbose XML configuration files.
-- Lots of libs favor XML vice JSON.
-- Deeply-nested code directories.
-- Class-centric model (only).
-- Verbosity and boilerplate.
-- Factories of factories of factories. :)
-- JVM install and config (many dials = control but also complexity).
-- Impractical / painful to write without an IDE.
-- Concurrency model, as compared to Go.
-- Std lib not matched to work I use Go for.
-- Memory consumption.
-- Performance (for the work I do).
-- Multiple inheritance (allows unnecessary complexity).
-- https://astaxie.gitbooks.io/build-web-application-with-golan... vs. https://medium.com/@ssaurel/create-a-simple-http-web-server-...
-- Oracle.
(edits: reformat pretty; compile times)
The beta of the last language I wrote had maybe half a dozen fans. I threw away the compiler/language because I didn't really know how to write a compiler back then. I'm writing a new one now
https://news.ycombinator.com/item?id=16548684 (2018)
https://news.ycombinator.com/item?id=6417319 (2013)
Discussed at the time: https://news.ycombinator.com/item?id=4158865