Using Java at the workplace for a couple years made me deeply appreciate the language. Some people seem to hate it based on aspects of the standard library that are foreign to me (something with FactoryFactoryBean) or outdated language elements that no longer apply (lack of first-class closures pre Java 8) but personally I’ve not faced such issues; neither during Android development, nor when I used it to write a few small network services.
I think the language hits a good spot between extremes. It has a straightforward C-based syntax. It’s very dynamic, with objects carrying type information at run-time, and classes also being run-time objects, but it has a static type system which probably aids in self-documenting code, and optimization. It’s garbage-collected, but doesn’t go overboard with “everything is an object” and instead lets primitives be primitives (int, double, Boolean, and so on). It uses single inheritance with interfaces (think protocols, contracts, whatever your favorite language calls it). The Streams API is crap in my opinion, and I advise using a few simple static functions for higher order operations like map and filter instead, just based on the good old Collection types. Here’s an example, implementing map; the definition of the Mapper interface is left as an exercise for the reader:
public static <I, O, C extends Exception> List<O> map(
Collection<I> inputs,
Mapper<I, O, C> mapper
) throws C {
List<O> outputs = new ArrayList<>(inputs.size());
for (I input : inputs) {
outputs.add(mapper.apply(input));
}
return outputs;
}
There are surely a couple more warts like the Streams API in the standard library, but all things considered it’s not a big deal in my opinion. I could go into detail on a few other language features that may be considered positive or negative, but this blog post isn’t a review of Java, so let’s cut it here.
I really don’t know how to best describe this feeling, but it just feels like Java fills the spot of a simple, straightforward language that’s high-level enough to let you focus almost purely on the problem domain, while still being down to earth enough to allow both for relatively efficient execution of programs written in it, and the creation of sane and readable code-bases that aren’t littered with uses of various obscure language features. Based on just the experience of reading and writing code in it (not the deployment and whatnot) I really really came to like Java.
This makes me ask: Why hasn’t a language like this replaced C a long time ago? Sure, despite all the optimizations in the JVM, code written in Java is usually going to be significantly slower. (According to the Computer Language Benchmarks Game, needing about 3 times as much time to perform the same tasks.) But the vast majority of code out there really doesn’t need much raw “language performance.” You’ll be waiting for disk I/O, network I/O, some operation the kernel is doing for you, or some operation already offloaded to a C library under the hood. Most new code brought into this world, I think, is glue code of one or another kind, even in “systems” programming. The added developer efficiency from having garbage collection and other such modern features arguably outweighs the small performance gains you’ll get from writing everything in C or C++.
Sure, there are attempts to replace C with something more modern, like Rust or Zig, but these languages still aren’t nearly as ergonomic as something like Java. We’ve seen from the recent System76 memory leak issues that performance can very quickly go down the drain if you make a memory management mistake, even in a language like Rust. (If I’m not mistaken, it was due to automatic reference counting, which fails to deallocate objects that cyclically refer to each other. A garbage collector would have had no issues with this.)
To be clear, languages as low-level as C, Zig, Rust etc. will always have their place obviously, at the lowest levels of systems programming, and in high-performance scenarios. The kernel itself, core libraries (especially if performing intensive computations), physics engines for video games, and so on. I’m thinking of slightly higher-level systems programming.
“Why not just use Java if you love it so much?” Well, it’s not much of a systems programming language. To run Java code, you need a JVM, which is quite heavy, and you can’t interface with C libraries all that easily. There’s also the warmup time of the JVM and whatnot. And then it’s more or less under corporate control. I really don’t think Java could or should ever replace C in the creation of, for example, systems utilities that you may invoke dozens of times within a single shell session.
No, what I want to see is a language that’s about as high-level as Java (meaning garbage collection, run-time reflection, and so on) but compiles to native code, can interface with C without too many hurdles, and only requires a single shared library to be installed on the system you deploy binaries to (whose size should be within the same order of magnitude as the size of glibc). This language could then be used to write a wide variety of programs including things such as small systems utilities (tools you’d invoke from the shell), package managers, init systems, desktop environments and applications, network applications, and so on and so forth.
There seem to be few contenders for this. OCaml is a big one. Another one is D. But OCaml has a very foreign syntax and leans heavily on the functional paradigm, which makes it strange to C programmers. And D has maybe just a tad too many low-level constructs baked into the language; from a cursory glance at it, I’ve gotten the feeling that you need to learn both the details of low-level languages like C, and high-level language features on top of that, if you want to use D effectively. My dream language shouldn’t have this requirement; a programmer should be able to comfortably use the language without knowing anything about lower-level languages like C, so long as they aren’t interfacing with C libraries or such.
Other contenders have even bigger issues, in my opinion. Go is obsessed with static linking, so for example I don’t think you’d want dozens of systems utilities all written in Go and statically linked to the libraries they use. SBCL seems similar to OCaml in performance, but suffers even more from the “strange language” factor. Same with Haskell, obviously. You may think of Swift, but it actually has no garbage collection. C# and Java can be compiled to native binaries, but it’s an after-thought and I doubt it will provide a good experience to a systems developer. Besides, there’s the corporate control issue with both.
There’s one language I need to read more on, to see how it fares according to my criteria, and that’s Julia. What I’ve heard and read so far was very promising. It’s a relatively new language; about 13 years old at the time of this writing.
I really wonder why a language like what I’m dreaming of hasn’t been created two or three decades ago already, to replace C in the “slightly higher level systems programming” landscape and beyond. I’m guessing the main reason is the difficulty of designing a good language and getting it to be adopted by C veterans.