Today marks the 5th anniversary of Rhovas! Five years ago, I outlined the first steps for what I wanted to create with a new programming language. Rhovas has come quite far from those original notes, so I thought it'd be a good idea to reflect a bit on the past five years and see how things have gone.
Additionally, to commemorate the day Rhovas now has an initial v0.0.1
release! This is largely to know what's active on the online editor, but over time this will
approach a full v1
release and come with more compatibility
guarantees. You can review the Release Notes
for a changelog and details on our versioning approach.
Finally, in combination with this release I've updated contributing guidelines to show issues (and PRs) are now accepted. I'm comfortable with the current direction of the language/compiler where contributions are manageable, and the language is stable enough where building programs and identifying bugs is highly valuable. If you're interested in helping, see FAQ: How can I help?.
A lot has changed over the past few years since the initial work on Rhovas. Since technically every feature is new, I think it's worth narrowing in on what's changed in the language design and how the compiler has evolved since the early days of Rhovas.
First up, language design. Honestly not too much has actually been changed here in my opinion (which is probably because I'm still playing catch-up with the compiler and many features are still unimplemented). Still, here's a few notable mentions:
- Error handling with both exceptions and results has turned out much better than I expected, though still needs a real trial-by-fire.
- First-class types have grown on me and feel very nice with
is
/as
/to
. - Similarly, functional forms like
as
/if
help with chaining and has led to me favoring suffix operations and more fluent styles. - Pattern matching received a big overhaul that included support for types, varargs, and predicates, plus a more generalized system that extends well to other types of data. I've also moved towards allowing pattern matching for all declarations, not just in match statements.
- Function overloading is now supported with stronger rules for 'safe' overloads (requiring disjoint types to avoid potential conflicts).
- Constructors now have a
this { ... }
initializer which improves ease of use and resolves problems between initialization order and inheritance. - Macros have been added, and syntax macros in particular have played a large role in steering the API design of the language towards enabling DSL support.
And secondly, the compiler. The current compiler was started just under two years ago and was at least the 7th or 8th attempt. Since then, my approach has significantly changed and I've worked my way through a lot of problems that I could never figure out at the time:
- ANTLR has been replaced by a handwritten recursive descent parser. This has been far faster to work with, allows more functionality like syntax macros and better error handling, and is overall easier to understand in my opinion.
- Static analysis (and consequently type checking) now exists! The new context system has played a huge role in enabling this, and I continue to learn more about implementing type systems and am slowly chipping away at bugs there. The current type system supports generics (including variance) and tuples/structs, but I haven't quite gotten to union/intersection types.
- There's now a standard library which is sufficient for implementing many leetcode-style programs (which has also led to Rhovas being added to RosettaCode). Time will tell if designing this from scratch was worthwhile (probably not), but this allows creating some practical programs.
- The compiler now supports Kotlin multiplatform to allow both JVM and JS targets, which is what allows the Online Editor to function. This has been a major improvement for enabling others to test out Rhovas and I even use it myself at times.
While there's still a lot to do (more on that later), I think this has been a great start and the majority of this has been accomplished in just the last year or two. Additionally, there's a lot of contextual knowledge and experience I've accumulated over that time as well, which I think is worth a section on it's own...
It'd be foolish to reflect on the past without calling out places for improvement, so here's a few words of advice moving forward I've picked up over the years. Note this is written more for future me rather than being general advice, so YMMV.
First, do something. A little bit of progress is always forward motion and over time will add up. Consequently, not everything has to be a large project or finished 'on time' - it's fine to take breaks every now and then. However, don't confuse taking a break with procrastinating because you don't know where to start!
In relation to Rhovas specifically, try to prioritize core language features rather than handling every edge case (including optimizing the software design of the compiler). This helps with making progress above (which in turn aids motivation) and the faster the language becomes usable the faster the feedback cycle is for ideation and bugfixes. Furthermore, the first attempt will always have a few quirks - it's more important that the code can be refactored later, and keeping strong test coverage around those new features will help with that in the future. In short, focus on implementing known use cases that are limiting rather than aiming for completion of some area.
It seems only fitting to end with what's next. To start, here's a few larger projects planned for the next few months:
- Inheritance, along with the ability to create interfaces. As a more object-oriented language this is a key part for being able to create useful abstractions, plus interfaces alone open up many more possibilities compared to class inheritance.
- Modularity, specifically the ability to compile multiple files and support namespacing between packages of a project. Needless to say, also important for larger projects.
- Standard Library 2 (Electric Boogaloo), to fill in many of the limitations of the current standard library. The largest feature missing is file/network IO, which allows communicating with external services and makes Rhovas much more viable for small scripting projects.
I hesitate to set plans too far in the future, but here are some of the 'nice-to-have' ideas that I'd like to see sooner rather than later.
- Mutability! This was one of the original goals of Rhovas and it's a shame it's taken so long to tackle.
-
Finish documentation for language features and the standard library. The existing documentation
only covers a portion of language features and hasn't been updated recently, and there's nothing
for the standard library yet.
- Related, a more structured tutorial/showcase would be really cool but probably isn't worth it in the foreseeable future.
- A Language-Server-Protocol (LSP) integration, which allows editor functionality like autocomplete, go-to definition, and other dynamic behavior. In theory, this should be possible to hook up to the online editor at some point as well.
- Integration with JS/JVM environments for compilation and standard libraries. Having access to Kotlin libraries (for instance) resolves most of the issues with standard library limitations. This is something I've experimented with multiple times but never really got working (main problems are scope/resolution differences and preserving correctness for static analysis).
I think that brings my reflection on the first five years to a close. It's been a lot of work to get here, and while there's still a lot left to do the language is in a far better state. Hopefully the next couple months will see some of the remaining core features come to light, and then I can finally start working on some real software projects in Rhovas!
As a small bonus, I've also added a brief FAQ section after this with some common questions I get asked (and a few fun ones). Take a look if you're interested, especially if you'd like to help!
Here's to five more years of Rhovas!
- Email:
WillBAnders@gmail.com
- Discord: Tag me in the
#blog
(or#rhovas
) channel!
Thanks for reading!
~Blake Anderson
Here's a handful of frequently-asked questions I tend to get, just for a bit of fun!
In short, I wanted to address common software development problems (particularly around API design and maintainability) and a language/ecosystem level solution seems like the best option. Rhovas already includes a few novel solutions for language design (such as syntax macros) and there's much more I'd like to tackle. Of course, this is also been a learning experience and I'm a lot more comfortable with other languages having researched a wide variety already.
The Motivation section of the Rhovas docs go into a bit more detail here, along with the problem statement which I call the Theory of Software APIs.
I think there's two ways to interpret this question:
- Favorite part of building of a language? Definitely seeing something work for the first time; it's incredibly satisfying to finally have a feature usable that has been just an idea for years. Seeing others experiment with that feature as well is really exciting too.
- Favorite part of Rhovas? I think pipelining is probably the best-designed feature; it solves multiple problems and is quite seamless. Honorable mentions go to syntax macros for embedded DSLs (which I've written a lot about) and exception-result isomorphism (still surprised this even works).
Anything with the type system, hands down. It's only a couple lines of code (mostly boilerplate) but the logic itself is incredibly complex. I often have to dig into problems for a while to convince myself whether something is an issue or things are actually working as intended with how different types interact.
Aside from that, having so many ideas that are still unimplemented is also a bit disappointing. It'd be great if I could experiment with the features I want to, but the implementation is so far away that it makes any further language design work largely speculative. Some day!
The online editor was a huge step forward for allowing others to try Rhovas without having to install the whole compiler. The whole repository had to be converted to a Kotlin multiplatform projects, and there were a lot of JS compiler bugs to work around. This also let into the Kotest refactor (which was a huge project in its own right) since JUnit only works with JVM, not JS.
Honorable mentions go to the Analyzer's context system, which opened the door for more advanced semantic analysis, followed by the recent improvements to test coverage, which has helped catch a lot of bugs and avoid many new ones.
Might as well cover some other stats while we're at it. At time of writing, there are:
244
commits (around3
/week)13,405
lines of Kotlin (and99
lines of Rhovas)1,178
unique tests (each runs on JVM and JS)12
GitHub stars (but who cares? haha pls star thx)- A questionably large number of hours I could have spent doing anything else
Haven't gotten that far? I've toyed around with ideas but nothing has really stood out yet. I typically use a scorpion for my Rhovas religion in Civilization VI, so I guess that's the closest we've been.
Writing Rhovas programs! In all seriousness, this is one of the best ways to help progress the language. Not only does it help find bugs and assist with prioritization, it's also helps with motivation for me to get you unblocked on whatever you're working on. If you don't have any ideas, Rhovas has a series of tests based on RosettaCode solutions and there's plenty of those available to try!
If you're more interested in the language design or compiler side, you can see if any of the GitHub issues stand out as something your interested in. If you have another idea in mind, feel free to discuss with me over Discord as well.