Interesting point of view - Golang might be pithily described as "Java done right". That has little to do with "systems programming" per se but can be quite valuable in its own terms.
Interesting point of view - Golang might be pithily described as "Java done right". That has little to do with "systems programming" per se but can be quite valuable in its own terms.
Is that supposed to be a jab? Because IME SBCL Lisp is in the same ballpark as Go (albeit offering a fully interactive development environment), and C++ is far from being the worst choice when it comes down to productivity.
The only reason is the AI winter, and companies moving away from Lisp.
for (item in collection) {
...
}
list.map { it + 1 }
fun printAll(vararg strings: String)
constructor(...)
companion object
I like the `for..in` which reads like plain English. Or `vararg` is pretty clear - compare that to "*" and the like in other languages. Or `constructor` can not be more explicit, there is no need to teach anyone that the name must be the same as the class name (and changed accordingly). Same is true for companion object (compare with Scala).[looks at the code bases of several recent jobs] [shakes head in violent disagreement]
If I'm having to open 6-8 different files just to follow what a HTTP handler does because it calls into an interface which calls into an interface which calls into an interface (and there's no possibility that any of these will ever have a different implementation) ... I think we're firmly into over-engineering territory.
(Anecdata, obviously)
If you wanted a mock, you needed an interface, even if there would ever only be one implementation of it in production.
You need to refactor it to use an interface just to unit test it.
Duck typing with python (e.g.) makes it easy to mock a class. One can subclass a Java class to make a mock, etc.
One can't do that in golang unless it's through an interface which then decouples tightly coupled code because [golang reasons].
I believe that's why people insert them everywhere, yes, but in the codebases I'm talking about, many (I'd say the majority, to be honest) of the interfaces aren't used for testing because they've just been cargo-culted rather than actually considered.
(Obviously this is with hindsight - they may well have been considered at the time but the end result doesn't reflect that.)
Java lends itself to over-engineering more than most languages. Especially since it seems that every project has that one committer who must be getting paid per line and creates the most complex structures for stuff that should've been a single static function.
Thus, the Java exception trace in the log file is almost like an interactive debug trace.
Whether that is a bug or a feature is an exercise for the reader.
You just happen to be looking at the wrong spot, see Kubernetes, YAML spaghetti, and plenty of stuff originated from Go in the enterprise space.
The "Bloated Abstractions" issue in Java is more of a cultural thing than an issue of the language. You could even say it's partially because early Java (especially before Java 1.5) was too much like Go!
Java used to have the same philosophy around abstractions, and Sun/Oracle were pretty conservative about adding new language features. To compensate for lack of good language-level abstractions, Java developers used complicated techinques and design patterns, for example:
1. XML configuration, because there were no attributes. 2. Attributes because there were no generics and closures. 3. Observers/Visitors/Strategies/etc. because there weren't any closures. 4. Heavy Inheritance, because there was no delegation. 5. Complicated POJOs and beans, since Java didn't have properties or immutable records.
No other language comes close to this: https://steve-yegge.blogspot.com/2006/03/execution-in-kingdo...
- Early Java projects before Java EE (like Applets and early GUI apps) did not have this level of complex abstraction. The code wasn't great (old Java APIs like StringBuffer and Date were often quite horrible), but it was simple.
- Java started getting complex with J2EE. J2EE was strongly motivated by enterprise requirements for interoperability and dynamically configurable and interchangeable components.
- Another source of complexity was the popularity of XML and the widespread belief (back in the early 2000s) that moving part or all of your business logic to XML was a good thing.
- And then there was the design patterns obsessions, where design patterns transformed from being a common pattern observed in code into something that should be emulated.
- Most of the early J2EE complexity is dead (EJBs, XML configuration, CORBA, SOAP), but many users don't want to give up component interoperability and powerful dynamic configuration. That's why Java frameworks like Spring, Java EE and even modern frameworks like Quarkus or Micronaut have all their annotations and implicitness.
- Go was luckier(?) to be born in a different time and popularized in a different context.
- There was no top-down enterprise-oriented backing for Go (even within Google, it was a bottom up project).
- The Go compiler, runtime and libraries were fully open-source from day one, under a permissive license. Third party libraries were also almost universally open source. The existence of open source culture made concerns about vendor lock-in moot, and interchangeability was not a thing.
- The XML hype died and there was a consensus that component wiring should be done in code.
- Many of us got sober on design patterns. Peter Norvig's critique[1] gained traction outside of the LISP community and the anti-design pattern view became dominant within the dynamic language community as well, even in strongly OO languages like Ruby. This is the community that was feeding Go.
- Most servers written in Go were just smaller in scope than the equivalent Java projects. This has many causes (less top-down big-rewrite enterprise projects, microservices gaining popularity, UI logic usually moving to front-end or mobile app).