My Thoughts on Programming Languages
Posted on Jul 3, 2021
In this post, I compare some languages I’ve worked with based on features that are important to me, then at the end, I decide my favorite language.
I don’t really keep a timer on how long I spend writing code, but I’d feel pretty comfortable claiming that I spent the following amount of time in the following various languages.
At the end, I rank languages with the features I care about.
I barely spent any time in V, but I feel comfortable making statements about it as it is a fairly basic language - more or less GO with some notable improvements.
It didn’t take me long to skim through the V documentation. There just isn’t much to V.
If you see a question mark in a field below, it probably means its been too long since I last used that language to remember if it has that certain feature.
Homoiconicity which in practice for me basically boils down to being able to rewrite the AST on the fly and is really important to me because its absolutely required to make a good DSL. For hardware design, languages such as Verilog or VHDL are quite simply in my opinion, the wrong tools and I think the ideal hardware design language could possibly be created as a DSL within an standard programming language.
First Class Package Manager
I also include first-class package manager as a feature to be considered simply because I dislike writing makefiles. I don’t consider scala SBT and or scala Mill as first class package managers because it’s more or less impossible to do offline builds with the tools. That is, if you’re trying to build
my_package which depends on
X which depends on
Y, you can’t easily grab packages
my_package, store them locally on your computer, disconnect from internet, and then build your scala binary. Its possible in theory, but extremely difficult in practice.
This makes scala a really terribly choice for offline work. In principal, this isn’t an issue for me as a have a reliable internet connection, but having a language environment that depends on an internet connection seems flawed by design.
Also, scala, java, and Rust require the package manager to run packages with dependencies. This would be like needing to run most of your python programs with
pip - just seems to be an odd design choice. It’s possible to build scala programs exclusively using
scalac, java programs with simply
javac, and rust programs with just
rustc in theory, but this is really difficult in practice. The scala compiler is incredibly slow, doesn’t support parallel builds, or build file caching by default. SBT and Mill can help with caching, but the caches they build seem to become currupted regularly.
Fast Compiler and Error Messages
Also, all these languages have really slow compilers except for Nim and V-lang.
Nim’s error messages aren’t very good. This hasn’t been an issue for me as I’ve been writing software for quite a while and can usually infer what made the compiler unhappy. But for a newcomer, this could easily be an issue.
As of this writing, I haven’t spent enough time in V-lang to make any claims about its error messages.
For me, portability means that said language can effortlessly be brought to a new architecture. C, C++, Python, Nim and V-lang all only require that the target architecture have support for Linux and the GNU C Compiler. This is important to me as I am a computer engineer by trade and strongly dislike the x-86 ISA. I like the ARM, RISC-V, OpenRISC, MIPS, and POWER ISAs and do my dev work nearly exclusively on POWER and ARM machines. It can be troublesome to get the other languages working seamlessly on architectures other than x-86 as they either require good JVM or LLVM for other architectures, which isn’t readily guaranteed.
Language Comparison by Features
|Language||Homoiconic||UFCS or Classes||Templates/Generics||Function Typing||First Class Package Manager||Package Manager Separate from Build Tooling||Scope Based Memory Management||Fast Compiler||Error Messages||Portable Score 1 to 3|
|Python||Not by design||Yes||Yes - Duck Typed||N/A||Yes||Yes||No||N/A||Very Good||3|
|Scala||Yes with Macros||Yes||Yes||Yes||No*||Not Practically||No?||Very Slow||Bad||2|
|Nim||Yes with Macros||Yes||Yes||Yes||Yes||Yes||Yes||Very Fast||Bad||2|
|Java||Not by design||No||Yes||Not Really?||No||Not Practically||No||?||?||2|
|Rust||Not by design||Yes||Yes||Yes||Yes||Not Practically||Yes||No||OK||2.5|
I think it comes down to Nim, V-lang or Rust. I really like the memory safety emphasis in Rust, but idiomatic Rust is so different from most other languages, that it still feels unnatural to me.
Nim is a nice all round language with really no major drawbacks.
V-lang is also very nice, I just wish it was homo-iconic. V-lang actually seems to be a perfect fit for bare metal work as does Rust.
Python is nice for quick and dirty prototyping, would never use Python in production, or for a computationally intense application(which accounts largely for most application I write - simulators, compilers, and such).
Rust is my favorite new language for the following reasons:
- encourages responsible and more purely-functional software paradigms such as those provided in Haskell and OCaml
- enables domain specific languages like Lisp macros
- enables the programmer to reason about performance like C
- supports baremetal nostdlib
- has good GUI libs such as Egui.rs
Languages I Wish to Avoid
C++ is first on this list as it has too many features. I don't typically start new codebases in C++, but I sometimes find myself working with existing C++ codebases. C++ has Cmake which I think causes more issues than it solves. I consistently have trouble building other people's codebases that rely on Cmake. C codebases seem to tend to have more robust build scripts than C++ codebases, and Rust, Nim, and Scala codebases seem to build reliably every time - except when they need to link to external libraries.
C is alright, but with V-lang, I get C without makefiles.
Scala as a language is fine. It is pleasant to use and has nice syntactic sugar. My only issues are the jvm, which still takes too long to start up and is very large in size - between 200MB and 1GB depending on the JVM config. This makes for very poor container spin up time, which is a problem as I often use containers to do unit testing.
Scala SBT and Scala Mill are also very unpleasant tools to use. Mill is worse than SBT. Simply running
mill --help will fire up an entire mill compile server before it realizes it only needs to print the help screen.
Java is too verbose.