A Survey of Native Programming Languages

In 2021, I started learning C, a goal I have had for a long time. It brought a level of excitement back to programming that I haven't felt since I was a complete beginner. I built Replicalc, a REPL-based calculator, experimented with developing a text adventure engine, and had a lot of fun. I always planned to do more with these projects, but eventually ran out of steam because…C is a pain.

I chose to develop these projects in C because I wanted to run them on both Linux and MS-DOS. That leaves few options, and C is likely the best documented and supported out of all of them. However, the reality is that C really sucks for application development, even on a small scale. It's a much slower, methodical, and error-prone process than I'd like it to be. I've enjoyed it and learned a lot from it, but it isn't practical enough for my purposes. It's time to look for something else.

What I really want is a language where I can be immediately productive and not have to worry too much about details like memory allocation. C, C++, and likely D are all too low-level for what I'm looking for. However, I don't want my software to require installation of a runtime or interpreter either, so that rules out popular high-level languages such as C#, Python, etc. Which languages lie in between these two groups?

Armed with a list of compiled languages from Wikipedia, I began looking through the options to see what suits my requirements. This blog post is an overview of the languages I've looked at, with my thoughts and impressions—it isn't intended to be an objective review.

Rust

Rust is a systems programming language that guarantees memory safety at compile-time—no garbage collector required. This makes it great for applications where security and reliability are essential, and it's lightning-fast. It's no wonder it's been voted the #1 most-loved programming language six years in a row on the annual Stack Overflow survey!

The first thing I noticed when I started diving into Rust is just how good the available documentation is. The Rust Book is thorough and well-written. Beyond that, there's an endless number of community guides and published books. Rust has experienced a surge in popularity over the past 5-odd years, so there's a massive community writing documentation and contributing to the Rust ecosystem.

On top of that, the development tools are outstanding, at least where VSCode is concerned. Its IntelliSense contains not only function and argument names, but also detailed descriptions and example code. There's a lot to like.

However, Rust has a steep learning curve. It's not insurmountable, but it's a different way of thinking to what I'm used to, and it's a bit off-putting. When it comes to my personal projects, I want a language that's dead-simple so that there are no barriers to me sitting down and writing code. Rust has a lot of potential, but it's decidedly not simple. I'd love to use it more in the future, but it's not the best choice for my current personal projects.

What I Like

  • Guaranteed memory safety
  • Large community
  • Excellent documentation

What I Don't Like

  • Complex, has a much steeper learning curve than most languages
  • Requires patience to learn

Links

Zig

Zig shows a lot of promise. It aims to provide a high level of safety while also remaining as simple as possible. In this way, it fills the middle-ground between C and Rust. It also has first-class support for integrating with existing C libraries, which should make it easy to use libraries like GTK.

I thought Zig would be a good choice for me. Safer than C, and simpler than Rust. However, I really don't like its syntax. It uses some odd choices that I can't get used to. For example:

  • Inconsistency: All documentation comments start with ///; unless they are top-level documentation comments, in which case they must start with //!.
  • Weird conventions: In a multi-line string, each line must be prefixed with \\.
  • Seemingly missing features: There is no conventional for loop for iterating over a number range. Instead, the best practice seems to be to wrap a while loop in its own block like this:
    {var i: i32 = 0; while (i < 10; i += 1) {
        %%stderr.printf("%i\n", i);
    }}
    Why would that be considered a better choice than this?
    for (var i: i32 = 0; i < 10; i += 1) {
        %%stderr.printf("%i\n", i);
    }
    There's probably a good answer—Zig seems to be well thought-out—but I don't know what it is.

I also feel like the documentation is a bit lacking. In its current form, it's more like an information dump than a well-organised manual. This is understandable because Zig is still very young and yet to reach version 1.0. I'm sure we'll see this improve as it matures. But in the meantime, I found it unwieldy and difficult to find what I'm looking for.

In summary, I like where Zig is going. However, the syntax feels somewhat alien to me. Between that and the current documentation, I don't have the patience to thoroughly dive into it now. Maybe I'll revisit it in the future.

What I Like

  • Achieves a strong balance between safety and simplicity, without using a garbage collector

What I Don't Like

  • Awkward syntax
  • No conventional for loop
  • Documentation isn't as organised as I'd like

Links

PureBasic

PureBasic ticks a lot of the boxes I'm looking for. It compiles to native, it's high-level, and it's really simple.

What I like most about PureBasic is how intuitive it is. I was able to get productive almost immediately, and spent the afternoon developing a console-based Wordle clone. The documentation is well laid out, although more examples would be welcome. The standard library has plenty of features, including an easy-to-use (if limited) GUI library. PureBasic's included IDE is decent, and features syntax highlighting, GUI debugging, and a form designer—everything you need to get started.

However, PureBasic is not without its flaws. The most glaring is that it's a proprietary and commercial product. I wouldn't have even considered looking at PureBasic if I didn't already have a license. I bought one in 2011 and completely forgot about it, so I'm approaching PureBasic now for the first time.

The license is affordable and include updates for life. However, I couldn't justify publishing my code under a Free Software license if it required a commercial compiler to use. In fact, it's difficult to justify paying for a compiler at all in 2022, when they are nearly all free of charge and Free Software.

There are also some curious absences. For example:

  • In console applications, there's no way to get the current cursor location. You can set the location—but you can't get it.
  • In windowed applications, there's no way to bind a procedure to a keypress unless you either:
    • Call the OS native API, which means sacrificing cross-platform compatibility; or
    • Create a menu bar, add a menu item, bind the menu item to a callback, and then bind each individual key as a shortcut to that menu item.
    You can see examples of both approaches in this forum thread, but neither are satisfactory to me.
  • In general, PureBasic doesn't allow expressions, statements, or function calls to be split across multiple lines. There is also no line continuation operator; this was apparently ruled out 20 years ago.
These all seem like reasonable features for an established, easy-to-use language to have. I don't understand why they are absent in PureBasic.

Desite its flaws, I don't mind PureBasic. It seems somewhat stagnant, but it's charming in its own way. It might be more appealing if I were developing commercial software, but otherwise the commercial license is a deal breaker for me.

What I Like

  • Simple, intuitive, and easy to learn

What I Don't Like

  • Not Free Software
  • Small development team and small community
  • Missing features
  • No support for the BSDs

Links

FreeBASIC

FreeBASIC is a close competitor to PureBasic. As implied by the name, FreeBASIC is Free Software—a welcome change. Even better is that it supports MS-DOS, which I had overlooked during my previous survey of DOS-compatible programming languages. If FreeBASIC can deliver the best parts of PureBasic, but with the advantage of DOS support and a Free Software license, then it would be a serious contender for my next language. To find out, I set to work rewriting my PureBasic Wordle clone in FreeBASIC.

Like PureBasic, FreeBASIC is immediately intuitive. Its syntax is very similar to Visual Basic (both are descendents of QuickBasic). The documentation is of a fairly high quality, and more detailed than PureBasic's. Where it falls a little flat for me is the inconsistency between how different runtime library features are implemented. Many of them are implemented as methods (subroutines or functions). However, others are statements. For example, files are opened for reading with a statement like this:

Opening a file in FreeBASIC
Dim fileNum As Integer = FreeFile
Open "filename" For Binary Access Read As #fileNum
I suspect FreeBASIC does this to maintain compatibility with QuickBasic. Personally, I prefer the method syntax used by PureBasic and most other languages. For example:
Opening a file in PureBasic
Dim fileNum = ReadFile(#PB_Any, "filename")

The main downside of FreeBASIC is debugging. FreeBASIC uses GDB. There is no official IDE with integrated debugger. Although you can configure VSCode to debug FreeBASIC code with GDB, I wasn't able to get this to work without breaking my app's console features. Even when debugging with an external terminal, colors wouldn't work properly and some key presses wouldn't get handled. I'm not sure why this happens—I didn't encounter this when debugging Replicalc and other C projects with VSCode and GDB. It's possible that I've just overlooked or misconfigured something, but it makes debugging tedious.

Other than that, I haven't encountered any significant issues or annoyances. For my purposes, FreeBASIC seems like an ok alternative to C, but I would need to get GDB and VSCode co-operating before I use it seriously.

What I Like

  • Free Software license
  • Solid documentation
  • MS-DOS support

What I Don't Like

  • Issues debugging with VSCode
  • Small community

Links

Pascal

I came across Pascal a few times during my previous survey for MS-DOS programming languages. The classic Turbo Pascal compiler is now freeware, and Free Pascal is Free Software. Delphi is still available (and still obscenely expensive). However, I really don't enjoy Pascal syntax, and the community is tiny—maybe even smaller than what's left of the BASIC community. Pascal is a non-starter for me.

Links

Nim

Nim looked like another good candidate. Its syntax is heavily inspired by Python, which is refreshing in a compiled language. The documentation is OK; it could be more detailed and better organised, but Nim is still relatively young.

Unfortunately, like FreeBASIC, I encountered issues with debugging in VSCode. This time it kind of worked—I could at least pause on breakpoints. However, the names of local variables would be garbled, and stepping over my code would inexplicably step into the Nim standard library instead. I tried following guides such as Jason Jones', but didn't have any luck fixing these issues. I would need to get debugging working properly in VSCode to give Nim a serious look.

What I Like

  • Free Software license
  • Python-inspired syntax

What I Don't Like

  • Issues debugging with VSCode

Links

Go

Go was designed at Google for productivity, simplicity, and concurrency. Concurrency isn't very important for my personal projects, but productivity and simplicity certainly are.

After spending some time going through the Tour and building some test projects, I was really impressed. Go is dead-simple; the syntax makes sense and actively improves on the conventional C style; and the VSCode tooling and debugging just works. In addition, I didn't encounter any significant shortcomings. Go is almost a simpler, garbage collected C. Maybe it's not so surprising considering Ken Thompson co-designed them both.

My only major criticism of Go is that I don't like its error handling. In Go, functions can return multiple values (also not a fan), so the final value is often used to indicate whether an error has occurred. The main problem is that you can't nest function calls with multiple return values unless the outer function accepts multiple arguments of the same type. Instead, you're forced to call the two functions separately. I much prefer the C way of doing things, where you check for an error by passing a pointer to an integer. While you can do this in Go, it's not idiomatic and has issues due to Go's unique language features. It's not a big deal at the end of the day, so I'll just get used to it.

I've now rewritten Replicalc in Go. Replicalc isn't complex by any stretch, but this has been a good project to familiarise myself with the language, and it's now in a much better state for further development.

What I Like

  • Simple, intuitive, and easy to learn
  • Large community
  • Decent documentation

What I Don't Like

  • Idiomatic error handling uses multiple returns

Links

Conclusion

Overall, I'm extremely happy with Go. It's almost exactly what I've been looking for: simple, high-level, and compiles to native. It's definitely going to be my go-to language for personal projects.

Rust has a tonne of potential. It would be my next choice after Go, and I'll likely use it in any projects where a garbage collector is unsuitable. If I could get past its syntax, Zig might be a good alternative; maybe it will grow on me.

PureBasic has a lot of charm, but its commercial license is a deal-braker. Nim and FreeBASIC are both superior, but I wouldn't consider using them unless I can get debugging to work in VSCode. And either way, I don't think they have much to offer me that Go or Rust don't.

Pascal is right out.

Do you have any thoughts or feedback? Let me know via email!