Ideas for a Programming Language
by Malte Skarupke
I’ve been simmering ideas for a programming language for a while. I’ll probably get around to actually writing one in a year or so. But Jonathan Blow is writing a new language right now and there is some overlap between his ideas and my ideas, so I kinda feel compelled to write about mine.
Since he just had a talk about compile time function evaluation, that will be my first post. But that’s not actually the order in which I want to write about things, so I’ll do a Star Wars move and post part 3 before posting part 1 and 2.
But still this post is a summary of what I will write:
Let’s write a faster high level programming language
I want a language in which I can write faster code than in C++. I can write faster code in C than I can write in Assembly, and I can write faster code in C++ than I can write in C. I think there is a next step in that chain. And I think the biggest factor for that is programmer productivity. For that my focus will be heavily on tools, static analysis and rethinking a few concepts for today’s hardware.
Part 1: Source Code in Database. (SCID) Storing source code as text puts an unnecessary tax on all tools development. The upsides of the initial convenience of using text files have long been outweighed by the long term downsides of having tools that are just kinda a bit awkward. Compilers are slower, Intellisense doesn’t quite work, there’s too much overhead for writing and running static analysis tools and diff tools are less smart than they should be partially because we store source code in text files.
Part 2: Rethinking the callstack. I think we could save half of all memory allocations if we changed how a callstack works. The callstack is a good abstraction, but it introduces a lot of lifetime problems because functions have to store their results somewhere before returning. Game developers write messy code just to save those memory allocations. Also I think that the popularity of garbage collection can partially be blamed on using a callstack. Other models exist (like coroutines or graph scripting a la Kismet) and we should consider how to use those in high level programming languages, or maybe just rethink what a good abstraction would be for today’s hardware.
Part 3: Getting rid of shadow worlds. This is inspired by a blog post by Gilad Bracha. A shadow world is when you create a domain specific language that isn’t quite as powerful as a high level programming language. And in C++ the preprocessor and templates are the biggest shadow worlds. You always end up wanting features of a high level programming language eventually, so why go into the shadow world in the first place? Just do everything in your high level language. (this is the part that I will write first because Jonathan Blow just had a talk in which he talks about very similar topics)
And these next parts I haven’t thought about a lot yet:
Part 4: Dependent typing. A few languages already have this: The idea of dependent typing is that your type can depend on run-time properties. With this you can make it a compiler error to index outside of the size of a vector because the compiler knows the possible ranges of values. The concept is promising but it will be very easy to make dependent typing annoying. So I’m not sure if I want dependent typing as it is implemented right now, but we do know a lot more about our variables at compile time than we express in the type system, and we should benefit from that. The main reason why we don’t benefit from that information right now is that making new types for all possible states would be annoying.
Part 5: Reactive Programming. I’m not yet sure if this has to be in the language or if you can do this well in a library. The big problem is that we spend much too much time on keeping all of our dependent state in sync. I’d guess that state errors are the most common errors. What you want is something like Excel where when you change a variable in one cell, everything that depends on that variable gets updated automatically without you having to write that propagation code for every single cell. (because that propagation code is where I see most bugs in C++) Libraries for this exist but I think you can only get universal support (and good performance) by making this part of the language.
Part 6: How to represent values. (this is a late part because I haven’t though about it enough yet, so this is subject to change) At this point it’s probably uncontroversial to drop inheritance in new languages. It just tends to lead to problematic design in the long term. Both Rust and Go are coming up with new concepts to do the things that we used inheritance for, and those concepts look like they’d be more maintainable. Similarly people are coming up with plenty of ideas for how to do automatic memory management without garbage collection or reference counting, so I think it might be reasonable to enforce automatic memory management even in a high performance language. I want a time traveling debugger and I am willing to make some trade-offs here to get it. (but not if I have to be slower than C/C++) And finally an idea worth thinking about is separating layout of memory layout from viewing memory, so that AOS to SOA transformations become transparent.
And possibly there will be more parts. I’m currently delaying actually writing this language for reasons, and if I actually write all these six blog posts (at my current rate of one blog post per month) then hopefully by that time I will be too impatient and just start writing the damn thing.
But I am certain that I will write this language. Thinking about the untapped potential out there, I can’t not write it.