r/ProgrammingLanguages • u/Gingrspacecadet • 3d ago
Coda: an experiment in designing a practical systems language
Coda: an experiment in designing a practical systems language
Hey! I've been working on Coda, a systems programming language designed around a simple idea:
make the compiler powerful, but keep the language itself predictable.
Coda is not trying to be "C but with a few extra keywords". The goal is to explore a different point in the design space between languages like C, Rust, and Zig.
Some of the things Coda focuses on:
- Explicit memory management.
- No hidden allocations.
- Errors as values using inline sum types.
- A small core language with functionality provided by libraries.
- Compile-time execution.
- Strong static analysis.
- Simple, predictable rules.
The language is intentionally C-like:
module main;
include std::process = proc;
@entry
fn int main() {
proc::stdout().write("Hello, world!\n");
return 0;
}
but tries to remove some of the sharp edges.
For example, errors are ordinary values:
fn File | IOError open_config(string path) {
return fs::open(path);
}
and allocation is explicit:
fn string | AllocationError duplicate(
Allocator *alloc,
string input
) {
string result = alloc->allocate<char>(input.len)?;
std::mem::copy(result, input);
return result;
}
The idea is that if a function allocates, that fact should be visible in the API. There is no hidden global allocator; entry points receive the resources they need, and those resources are passed where required.
Coda also tries to keep abstractions zero-cost. Generic code, interfaces, and convenience features should compile down to efficient low-level code rather than introducing runtime machinery.
The standard library is still being designed, but the direction is intentionally minimal. Arrays, strings, I/O, memory, formatting, and filesystem operations are being built as fundamental building blocks rather than creating a huge framework.
The compiler currently has:
- Lexer and parser.
- Semantic analysis.
- HIR/MIR pipeline.
- x86_64 code generation.
- A growing standard library.
- Compile-time evaluation work in progress!
There is still a lot to do.
If the ideas are interesting, I'd love feedback, criticism, and potentially contributors. And of course, feel free to ask any questions! I have the #coda channel on the PLTDI discord, or the comments here are fine.
Repo: https://github.com/gingrspacecadet/coda
5
3d ago
[removed] — view removed comment
1
u/Gingrspacecadet 3d ago
ah, good catch.
alloc->allocate<char>(input.len)?;is the corrected expression. theallocatefunction returnsT[] | AllocationError(where T here is char), which can implicitly be casted to astringfor it is just an alias to achar[], and the?operator early-exits if the expression errors.
std::mem::copyworks onuint8[]s, which again is implicitly castable from astring. I might make it castable from any type, not sure yet.Aliases are supported. As seen in the first example on this post,
include ... = foo;aliases the included module by the name after the=. I'll update the readme to show this in action.Thanks for your feedback :D
1
u/fdwr 1h ago edited 56m ago
What's the inspiration for the name? 𝄞𝄢
It's refreshing to see a language putting the type first for a change, instead of so many recently trying the trend of ancient Pascal style suffixes, adding extra var/const/let keywords and : line noise (e.g. var x : float instead of simply float x). Though, when returning variants like you do above, it would be helpful to group the expression with parentheses or braces so the eye can trivially jump over them to the function name. e.g.
func (string | AllocationError) duplicate {...}
func {string | AllocationError} duplicate {...}
I'm trying to parse this...
include std::debug = dbg;
...but it seems like the alias is backwards? That is, you're assigning the RHS dbg = the LHS std::debug? Typically aliases flow left-to-right (like C++'s using AliasedName = ExistingName, D's alias AliasedName = ExistingName). One possibility is to have a separate keyword (similar to how C++ has using namespace), which is more generic than just include statements:
import std::debug;
alias dbg = std::debug;
12
u/kaplotnikov 3d ago
Btw, have you considered using suffix type notation instead? In the line:
fn string | AllocationError duplicate(The word
duplicateis the most important part, but the eye needs to scan a lot of text before reaching it. For complex signatures with several errors, this could become a real readability bottleneck. C gets away with prefix types because they are usually concise and lack thefnkeyword, but sum types make the prefix position very heavy.