It is mostly for fun and exercise in language design, I don't have any grand aspirations for it. It is however, by this time, a usable general-purpose language.
Alumina borrows (zing) heavily from Rust, except for its raison d'être (memory safety). Syntax is a blatant rip-off of Rust, but so is the standard library scope and structure.
Alumina bootstrap compiler currently compiles to ugly C, but a self-hosted compiler is early stages that will target LLVM as backend.
If that sounds interesting, give it a try. I appreciate any feedback!
Standard library documentation: https://docs.alumina-lang.net/
Online compiler playground: https://play.alumina-lang.net/
Well-established languages have tons of widely used, highly vetted libraries implementing functionality that doesn’t exist in the new language and would be totally impractical for an individual to implement themselves. For example, if I’m doing scientific computing, I need a good linear algebra library like Eigen or Armadillo (or Numpy/PyTorch/Tensorflow), all of which would be impossible for me to implement myself.
Therefore, for a new language to catch on, it needs a good foreign function interface. Yet FFIs are almost never brought up whenever new languages are discussed on HN. (Again, I realize that this is just a fun project and widespread adoption is not a goal.)
[1]: https://zenodo.org/record/4711425
[2]: The paper should be up in a few days I suppose.
Check for example the language bindings to LLVM's C API (fairly low level) and Tree-Sitter which is used internally (a bit higher level bindings)
https://github.com/tibordp/alumina/tree/master/libraries/llv...
https://github.com/tibordp/alumina/blob/master/libraries/tre...
I think UFCS makes it quite nice for bindings, since external C functions can be used as if they were methods if the object is passed as the first parameter. So in many cases there might not even be a need to write wrapper structs for bindings that feel native.
Of course, it's still a manual process and since Alumina is just a compiler and stdlib for now (no llibrary ecosystem, no compiler driver), it's a bit cumbersome. But I like the approach Rust has with bindgen and cc crates, to automatically create bindings for C and C++ code.
Nim, Zig and Rust do too, but they have semantics much closer to C, so it’s almost free (especially when they can all use llvm directly for code generation, and Nim’s preferred way is to compile through C in the first place)
import stdio;
void main() { printf("hello from D!\n"); }
is all that's necessary, as D has a built-in C compiler that reads stdio.h, compiles it, and presents its interface to the D code.Do you really intend to write your own library for a given task when there is a perfectly good and mature library that does that task and more freely available?
How long would it take for you to write your own Sqlite or your own Nginx?
Also, wonder how long it'll take until we see a "Aluminia" fork...
It's full of unnecessary noise and additionally very irregular. (With complete craziness thrown in between like the semicolon rule to "visually distinguish" procedures and functions, which must be a kind of joke I don't get).
I really don't understand how such a conceptionally well thought out language got this pretty ugly syntax.
(And no, you don't need such ugly syntax "because language features". Just have a look at Scala 3 that is much more powerful but maintains a clean, almost pythonic syntax).
Using "<" and ">" as both operators and delimiters
Turbofish
Symbols instead of words (ref -> &, and -> &&, not -> !, …)
Inconsistency (Why [i64; 5] and not something like array<i64, 5>?)
From what I gather, it might be more accurate to say that Alumina has no ownership model. Rust requires manual memory management, but offers the ownership model as a compile-time tool for doing so.
It would actually be pretty interesting to see some experimentation around alternative ownership models.
IMHO the way it’s used in Go is a workaround, of luck of destructors, not a feature.
Edit: not a criticism on your language OP, which is better than what I could have ever built. Just a comment in the “defer” trend.
I think one case where defer might be nicer is for things that are not strictly memory, e.g. inserting some element into a container and removing it after the function finishes (or setting a flag and restoring it).
This can be done with a guard object in RAII languages, but it's a bit unintuitive. Defer makes it very clear what is going on.
Some syntactic sugar, like Python’s “with” should help with that, shouldn’t it?
let stream: &dyn Writable<Self> = if output_filename.is_some() {
let file = File::create(output_filename.unwrap())?
defer file.close();
file
} else {
&StdioStream::stdout()
};Basically, it is going to be a full-featured Compiler front-end foundation library with incremental parsing capabilities, error recovering, AST manipulations, etc, but written entirely in Rust, and hopefully with more user-friendly API for Rust devs.
May I ask you to give me some feedback on your experience with Tree Sitter, and the challenges you faced during the development of your compiler's front-end?
Thanks in advance!
The only two pain point I had is that the `node-types.json` that's generated only contains the names of the nodes, not the numerical IDs. This means that if you have some codegen generating Rust enums is difficult if you want to avoid matching nodes by string.
I wrote https://github.com/tibordp/tree-sitter-visitor for generating visitor traits in Rust for a given grammar. I actually did it a bit differently in the end for Alumina, but it might come useful.
How difficult was it for you to design a whole programming language?
Do you have a theoretical CS background?
If I want to design my own, would learning Racket and other LISPs help?
I'm interested in formal methods and embedded systems.
Protocols were probably the trickiest feature of the language to figure out. As for the compiler itself, surprisingly, the biggest hurdle to get over was the name resolution. It's a tiny part of the compiler today, but everything else was much more straightforward.
I don't have formal CS background, but I have been coding for a long time. I read the Dragon Book and would recommend it to anyone writing a compiler, even though it's a bit dated.
I don't know Racket or LISP myself so I cannot comment on that part.
The sandbox is running the code server-side in a nsjail container.
As for unwrap, I feel you! the try expression (expr?) is supported, which makes it look a bit nicer, but I'm still trying to figure out a good idiom for when you actually want to do specific things based on whether the result is ok or err.
Alumina does not have Rust-style enums (tagged unions) or the match construct, which makes it a bit tricky.
What if Rust provide a separate analyzer to analyze potential memory leak from this language.
The only potential problem I see that with the current C backend, the debugging information is very hard to trace back to the original Alumina source code, so it might be hard to see where the leaks are coming from. This is something I plan to address in the self-hosted compiler, once it is functional.
Were there similarities besides "syntax inspired by Rust"?
The overarching theme is to see how far you can go making a language that feels high level without having a garbage collector or RAII. I used to use Deplhi/Pascal a lot when I was younger and it was this kind of language.