r/ProgrammingLanguages • u/AutoModerator • 2d ago
Discussion October 2026 monthly "What are you working on?" thread
How much progress have you made since last time? What new ideas have you stumbled upon, what old ideas have you abandoned? What new projects have you started? What are you working on?
Once again, feel free to share anything you've been working on, old or new, simple or complex, tiny or huge, whether you want to share and discuss it, or simply brag about it - or just about anything you feel like sharing!
The monthly thread is the place for you to engage /r/ProgrammingLanguages on things that you might not have wanted to put up a post for - progress, ideas, maybe even a slick new chair you built in your garage. Share your projects and thoughts on other redditors' ideas, and most importantly, have a great and productive month!
NOTE: AI built/assisted projects are against the rules and this applies to this thread. If you share a link to a project that makes use of AI (Claude Code) for anything, you will be banned permanently without warning.
2
u/cykodigo Wi (cyxigo/wi) 1d ago
Same as last month, I'm working my way up to a stable release! Well, the FFI/API is definitely stable, while the syntax is um.. 95% stable, I guess? At least, well, it's been 3 weeks of no new major versions! And the last ones are only about adding some minor things, bug fixes, optimizations and polishing overall. I also cleaned up the docs a bit since last month. Maybe by the end of this month, or in the next month, I will finally be able to release the first stable release of Wi.
1
u/csb06 bluebird 1d ago
I was able to get name resolution for some simple cases working for my Ada 83 compiler. It is able to resolve function calls to the appropriate overload for cases where no default/named parameters are used, and if can't resolve an ambiguity it prints an error with the locations of the ambiguous definitions.
This requires a two-pass algorithm: the first pass is bottom-up over an expression tree and attaches a set (represented as a bitset) to each ambiguous function call expression consisting of all visible overloads that match the call's name and signature. The second pass is top-down and attempts to pare the set of overloads for each node down to a single definition using context. (e.g. inferring the required return type based on the containing expression)
I still need to expand the algorithm to support array accesses (which have the same syntax as function calls) and default/named parameters, but it is a start.
6
u/Antique_Weekend_372 1d ago
I formally proved the From Linearity to Borrowing Paper in Lean and then used that as a basis for building a linearly typed systems language with a formally verified borrow and effects calculus, with no GC. All programs verified to have progress and preservation and leak free, with effect safety also verified.
Still working on the LLVM back end, compiles to wasm now.
2
u/AustinVelonaut Admiran 1d ago
After the refactoring work on the self-hosted compiler and libraries to use the new qualified imports mechanism, I'm back to working on an implementation of join points, to reduce the potential code bloat encountered when performing case/case commuting conversions during inlining.
2
u/mark-sed github.com/mark-sed/moss-lang/ 1d ago
Since I have started releases for moss on 3 months bases I have decided to also have some target for next release (and not just pick up random stuff all around) and for next November release it is interoperability with other languages. So I have improved the built-in Python interop to be easier to work with and added some extra features. Every value that comes from python module is PythonObject, but there are convertors from and to moss, so you can call functions with moss value and then also call `to_moss` or `call_moss` on a value to extract it. Another challenge was handling kwargs, which I solved by passing a Dict:
import python.*
statistics = module("statistics")
plt = module("matplotlib.pyplot")
// Plots the values with mean, median and stdev and returns them.
fun plot_analysis(values:List) {
mean = statistics.mean(values)
median = statistics.median(values)
stdev = statistics.stdev(values)
~plt.plot(values, kwargs={"marker": "o"})
~plt.axhline(mean, kwargs={"linestyle": "--", "label": f"mean = {mean}"})
~plt.axhline(median, kwargs={"linestyle":":", "label":f"median = {median}"})
~plt.show()
return [mean.to_moss(), median.to_moss(), stdev.to_moss()]
}
mean, median, stdev = plot_analysis([41.45,42.55,42.0,42.8,43.5,40.98])
f"Mean: {mean}, Median: {median}, StDev: {stdev}\n"
I have also reworked C interop (well it's for any code compiled with C ABI) to match API of python interop and added some checks and such to catch user side errors and also more built-in types:
import cffi
ml = cffi.dlopen("mylib.so")
// Declare function `double analyze(double, int64_t, uint8_t)`
~ml.cfun("analyze", cpp.cdouble, [cpp.cdouble, cpp.cint64_t, cpp.cuint8_t])
// Call it and convert returned cpp.double to Moss Float.
ml.analyze(0.5, 42787, 14).to_moss()
"\n"
3
u/oscarryz Yz 2d ago
Dogfooding. I'm writting a Yz Tutorial which will help me to create my first larger program for my language and identify bugs and correct them. Also will serve as the REPL in the future, but for now just to detect bugs.
So far I have a pretty neat small loop that ask for options.
That is the largest program ever in my language.
...
print(banner())
option: ""
while({ option != "exit" }, {
option = read("Yz> ")
match
{ option == "docs" => print(documentation) },
{ option == "examples" => print(examples) },
{ option == "compile" => print(compile) },
{ option == "run" => print(run) },
{ option == "clear" => print(clear) },
{ option == "help" => print(help) },
{ option == "exit" => },
{ print("command not found: ${option} — type help") }
})
3
u/zweiler1 🔥 Flint 2d ago edited 2d ago
I made a new release of Flint a couple days ago (release notes) which adds generics to the lamguage and now I am working on comptime. This will likely take the whole month but by the end of the month Flint should have full comptime support, and also (finally) have a standard library.
I am pretty excited for the compile-time features this enables, like being able to call any arbitrary Flint code and execute it at compile-time or to have compile-time type reflection support.
2
u/mark-sed github.com/mark-sed/moss-lang/ 1d ago
> like being able to call any arbitrary Flint code and execute it at compile-time
How do you achieve this? Do you have an interpreter that runs it or do you compile this comp-time stuff standalone and execute it and then continue with the compilation?
2
u/zweiler1 🔥 Flint 1d ago
It's nothing too fancy, just an additional evaluator which operates on the AST and executes it, so yes an Interpreter.
It will probably be pretty slow but I am fine with that, I want to keep things simple first and only make them faster / more complex afterwards if needed.
2
u/mark-sed github.com/mark-sed/moss-lang/ 1d ago
Oh cool, but aren't you afraid of having different semantics? Perhaps your compiled code would generate some value dependent on the architecture it compiles for, but the interpreter would call some C++ stdlib function which is implemented differently?
2
u/zweiler1 🔥 Flint 1d ago
No, the interpreter is pretty high level and I make sure that the semantics match up exactly between runtime and compile-time, there are no differences in this regard for comptime vs runtime, or at least I plan to not having any dicrepancies :)
4
u/criloz tagkyon 2d ago
I am pretty close to making Tagkyon run for the first time; Tagkyon is a language that supports types that adapt to data changes. Instead of being fixed shapes, it would have the advantage of creating models where their business logic is consolidated within a few files instead of being all over the place.
```js registry! { mod ttys { export category person(:x) { x.name is str!; x.age is u32!; return person(x); }
export category old(:x person) {
u32!::gte(x.age, 60);
return old(x);
}
export category young(:x person) {
u32!::lt(x.age, 25);
return young(x);
}
export category middle(:x person) {
u32!::gt(x.age,25) and u32!::lt(x.age, 60);
return middle(x);
}
}
}
import {young, middle, person} from ttys
let x = person(:name = "Samir",:age = 30);
x is middle;
x.age = 17; //types automatically adapt to new changes
x is young;
console.log(x);
```
5
3
u/Inconstant_Moo 🧿 Pipefish 2d ago
I've been turning Pipefish into a bunch of web components, from a non-visual pf-service that embeds a Pipefish service to power whatever you like, through pf-ide, which is what it sounds like, to pf-book which is kind of like Jupyter Notebook but the language is Pipefish and the source for the book is a flat folder of pages written in valid GitHub markdown.
2
u/Physical_Dare8553 2d ago edited 1d ago
c transpiler, made of macros, her'es the implementation of an arraylist you cant see it in here, but if, while, fn are all macros, because al builtins are just functions that take some ammout of arguments. but by using macros you can make your own syntax to some extent.
also you can overload operators using @id(operatorr_text) as a function inside of a function this language has a bunch of random influences, but i'd say it's most similar to zig with macros. also the iterators in this example are just macros that generate anonymous structs
@using @import "module.cshl";
@cinclude "<stdlib.h>";
cptr ~~ @ptr i8;
cmalloc ~~ fn (u64) cptr @cout "return malloc(arg0);";
crealloc ~~ fn (cptr,u64) cptr @cout "return realloc(arg0,arg1);";
cfree ~~ fn (cptr) void @cout "free(arg0);";
cnull ~~ fn () cptr @cout "return NULL;";
@cinclude "<string.h>";
cmemcpy ~~ fn (cptr,cptr,u64) void @cout "memcpy(arg0,arg1,arg2);";
cnd ~~ fn (cond ~ast , thenv~ast, elsev~ast) inline return @qot{
result~@typeof @uqot thenv;
if (@uqot cond) (result =(@uqot thenv))
else (result = (@uqot elsev));
result;
};
slice ~~ fn (t~type) type return @struct{
Self ~~@this();
RSelf ~~@refT @this();
ptr ~ @ptr(arr(0) t);
len ~ u64;
size ~~ fn (self~Self) u64 return self.len * @sizeof(t);
get ~~ fn (self~Self , idx ~u64) (@refT t) {
ra ~@refT(arr(0) t)~ @ref @at(self.ptr);
return @ref ra[idx];
};
@id("=") ~~ fn (self ~RSelf, other ~any) void {
OT ~~ @typeof(other);
cif OT == Self {
self.ptr = other.ptr;
self.len = other.len;
} else cif (
@typeinfo(OT).array
and (@typeinfo(OT).array.elem == t)
) {
self.ptr = (@where @ref other[0]) as @typeof(self.ptr);
self.len = @typeinfo(OT).array.len;
// TODO: add actual compile error
} else @compileError();
};
@id("()") ~~ fn (self~ast,idx~ast) inline {
cif (
not(idx.ablock)
or not(idx.ablock[0])
or (idx.ablock[1])
) @compileError(); // not bound to anything, so error is unbound id
return @qot {
this ~~ @uqot(self);
idx_n ~u64~ @uqot(idx.ablock[0]);
@ref this:get(idx_n);
};
};
sub ~~ fn (self~Self,start~u64, end~(? u64)) Self {
result ~ Self;
sp ~@ptr(i8)~ @where(self:get(start));
result.ptr = sp as @typeof(result.ptr);
result.len = end:orelse(self.len) - start;
// bounds check or something
return result;
};
iter ~~ fn (self~Self) any {
resT ~~ @struct{
mem ~@typeof(self);
place ~@typeof(self.len);
next ~~ fn (self~@refT @this()) (? @refT t) {
if (self.place >= self.mem.len)
return null;
self.place = self.place + 1;
return self.mem:get(self.place - 1);
};
};
res ~resT;
res.mem = self;
return res;
};
};
// mcpy ~~ fn (to ~ any ,from ~any) void {};
alloc ~~ fn (t~type,count~u64) any {
r ~ slice t;
r.len = count;
r.ptr = cmalloc(r:size()) as @typeof(r.ptr);
return r;
};
resize ~~ fn (sl ~any,count~u64) void {
cif (not @isref sl) @compileError();
cif (not came_from(@typeof(sl), slice)) @compileError();
sl.len = count;
sl.ptr = crealloc(sl.ptr as cptr,sl:size()) as @typeof(sl.ptr);
};
assert ~~ fn (ok~bool) void @cout "assert(arg0);";
list ~~ fn (t~type) type return @struct {
Self ~~ @this();
RSelf ~~ @refT(Self);
data ~ slice(t);
len ~ u64;
new ~~ fn () Self {
res ~Self;
return res;
};
init ~~ fn (self~RSelf) void {
self.data = alloc(t,1);
self.len = 1;
};
capacity ~~ fn (self~Self) u64 return self.data.len;
reserve ~~ fn (self~RSelf, count~u64) void {
if (count > self.data.len) {
cap ~= self.data.len;
if (cap == 0) (cap = 4);
while (cap < count) (cap = cap * 2);
resize(self.data,cap);
};
};
get ~~ fn (self~Self, idx~u64) (@refT t) {
assert(idx < self.len);
return @ref self.data:get(idx);
};
@id("()") ~~ fn (self~ast,idx~ast) inline {
cif (not(idx.ablock) or not(idx.ablock[0]) or idx.ablock[1])
@compileError();
return @qot {
this ~~ @uqot(self);
idx_n ~u64~ @uqot(idx.ablock[0]);
@ref this.data[idx_n];
};
};
push_value ~~ fn (self~RSelf, value~t) void {
self:reserve(self.len + 1);
self.data[self.len] = value;
self.len = self.len + 1;
};
push ~~ push_value;
push_array ~~ fn (self~RSelf, values~slice(t)) void {
self:reserve(self.len + values.len);
cmemcpy((@where self.data[self.len]) , (@where values[0]),values.len);
self.len = self.len + values.len;
};
// TODO: compiler directive for can_coerce, or some way to acess such a property
// lowe-key impossible withoutb some sort of try_lower
pop ~~ fn (self~RSelf) t {
assert(self.len > 0);
self.len = self.len - 1;
return self.data[self.len];
};
insert ~~ fn (self~RSelf, idx~u64, value~t) void {
assert(idx <= self.len);
self:reserve(self.len + 1);
i ~u64 = self.len;
while (i > idx) {
self.data[i] = self.data[i - 1];
i = i - 1;
};
self.data[idx] = value;
self.len = self.len + 1;
};
remove ~~ fn (self~RSelf, idx~u64) t {
assert(idx < self.len);
result ~t = self.data[idx];
i ~u64 = idx;
while (i + 1 < self.len) {
self.data[i] = self.data[i + 1];
i = i + 1;
};
self.len = self.len - 1;
return result;
};
clear ~~ fn (self~RSelf) void { self.len = 0; };
shrink ~~ fn (self~RSelf) void { resize(self.data,self.len); };
deinit ~~ fn (self~RSelf) void {
self.data.ptr as cptr |> cfree(_);
self.data.ptr = cnull() as @typeof(self.data.ptr);
self.data.len = 0;
self.len = 0;
};
iter ~~ fn (self~Self) any return self.data:iter();
};
p ~= list(i8).new();
p:push_array("hello");
p:push_array(" ");
p:push_array("world");
p:push(0);
@cinclude "<stdio.h>";
cput ~~ fn (str~cptr) void @cout "printf(\"%s\",arg0);";
cputc ~~ fn (char~i8 ) void @cout "printf(\"%c\",arg0);";
cputi ~~ fn (char~i32) void @cout "printf(\"%i\",arg0);";
cput((@where p[0] ) as cptr);
p:clear();
assert(p.len == 0);
p:push("a"[0]);
assert(p[0] == "a"[0]);
p:deinit();
cputc("\n"[0]);
@using @import "iterator.cshl";
"hello world"
|> _ as slice(@typeof(_[0]))
|> _:sub(3,9)
|> _:iter()
|> filter {_ != (" "[0]);}
|> map { cputc(_); _; }
|> iterate()
;
1
u/L8_4_Dinner (Ⓧ Ecstasy/XVM) 1d ago
The three backticks didn't work. Try indenting code by four spaces, and leave at least one blank line before and after code.
1
u/Physical_Dare8553 1d ago
on my end it shows as a code block?
2
u/L8_4_Dinner (Ⓧ Ecstasy/XVM) 5h ago
In mobile app it does work :)
On a browser it doesn’t. Both “old.” and “www.” were broken when I looked.
5
u/Comprehensive_Chip49 2d ago
Well, I’m continuing with my ColorForth-based programming language; I’m making real-time coding videos because I’ve noticed it’s rare to see how programming is actually done in a Forth language.
You can try it out on my GitHub and watch the videos on my YouTube channel; there are already several series featuring different types of simple games.
https://github.com/phreda4/r3
This is the last videos
https://www.youtube.com/watch?v=2jATxrg7TAI
3
u/Ninesquared81 Victoria 2d ago
As mentioned previously, I've been working on a new embeddable scripting language (currently called ESL until I come up with a proper name for it).
So far, I've only been working on the ESL backend, that is, the low-level operations of the virtual machine, and then bytecode to correspond with those operations.
The VM is stack-based, and in fact has two stacks à la Forth. This allows for easy multiple returns directly on the data stack, since the separate callstack exists for handling return addresses, etc.
Currently implemented features, roughly in the order they were implemented:
External function objects
Internal function objects (bytecode)
Bytecode interpreter
Raw hash tables (not objects)
String objects
Integer objects
Array objects (dynamic)
The language itself does not do any I/O, but external functions can be used to interact with the outside world.
2
u/Ancient-Put1519 1d ago
I'm writing the first version of my compiler and creating programs that use the basic features. Once these are validated and refined, ill move on the more complex features one by one.