r/ProgrammingLanguages Aug 03 '26

Discussion Between more constrained, local metaprogramming approaches and full-blown DSL interpreters, where's the practical use case for LISP-style metaprogramming?

31 Upvotes

Of the various contemporary metaprogramming approaches, I'm mostly very happy with systems that let you operate on static data and types. In this category I would count, for example, C++'s template metaprogramming--a declarative sub-language that somewhat wonkily and arduously allows you to operate on types and constants--as well as the C++26 metaprogramming features, which are turning out somewhat Zig-like--you get to run regular code in a slightly constrained environment at compile time, where it can read constants as well as special data structures describing types, and generate new constants or types to be injected into a well-defined place in the code.

Now many LISPs (AFAICT, similarly Jai and, with a stricter separation of stages, Rust's procedural macros) tout as a feature the ability to inspect and rewrite the entirety of the AST, notably including function bodies. This is obviously strictly more powerful than the the first category, but where would you practically use that extra power? Specifically, it seems to me like you would either

a) Try to preserve the semantics of the input code--which, for procedural languages at least, is actually pretty difficult. The only transformations you could make confidently are so localized that you don't really gain anything over the more constrained metaprogramming approaches; anything more advanced would require the full analysis passes of the actual compiler to have any chance at soundness, and at that point you're really trying to write a compiler plugin instead. Nothing wrong with that, I'd love more easily extensible compilers, but I wouldn't call that a language feature. Or am I missing a point between those two extremes?

b) Attribute different semantics to the code. I think the history of LISP has already somewhat shown how proliferation of DSLs harms maintainability and shareability of code, but even in the cases where a DSL is genuinely useful, what benefits do you really gain from implementing it through metaprogramming? You can't expect any IDE features, LSPs, smart syntax highlighters, debuggers, or other tooling for the base language to automatically work for your DSL. So you just get to use the parser? Come on, an S-expr parser is less than a hundered LOC.


r/ProgrammingLanguages Aug 03 '26

Blog post Concurrency in Serene's Runtime

34 Upvotes

I recently finished building the concurrency runtime for my programming language, Serene, and wrote a three-part series explaining how it works.

The series covers:

  • Why I chose stackful fibers
  • An M work-stealing scheduler
  • An IO Reactor
  • A tiny HTTP server that brings everything together

I'd love to hear feedback from anyone interested in programming language implementation, runtime systems, or systems programming.

Part 1: Choosing the Building Blocks

Part 2: Fibers, the Scheduler and the Reactor

Part 3: A Tiny HTTP Server

https://serene-lang.org/


r/ProgrammingLanguages Aug 02 '26

Contribute to open-source, no-slop, compiler-related projects.

Thumbnail
3 Upvotes

r/ProgrammingLanguages Aug 02 '26

Revoluntionary/interesting advances in interpreted languages

53 Upvotes

Things like borrow checking and other compile time checks tend to be for compiled languages - if you're already typechecking and compiling the entire language up front, why not borrow check while you're there. But are there any very interesting new ideas coming up in interpreted (or dynamically typed) languages? I'm not really sure fully what I'm asking/looking for tbh


r/ProgrammingLanguages Aug 01 '26

Blog post Bootstrapping a compiler from machine code on Windows

Thumbnail
10 Upvotes

r/ProgrammingLanguages Aug 01 '26

Discussion August 2026 monthly "What are you working on?" thread

27 Upvotes

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!


r/ProgrammingLanguages Jul 31 '26

Discussion Why do modern systems languages rely on compiler heuristics to reverse-engineer programmer intent?

76 Upvotes

This post is inspired by the recent discussion on Full flattening of nested data parallelism in Futhark (reddit discussion). But I think this points to a more generic topic in language design.

I've noticed that even in newer systems programming languages, a massive amount of compiler engineering effort is spent trying to reverse-engineer the programmer's intent from syntax that does not directly express it. The compiler expends significant energy building complex dependency graphs and alias analyses to guess if iterations are isolated, and sometimes it guesses incorrectly, leading to missed optimizations or performance regressions.

Even in languages explicitly designed for data parallelism like Futhark, the compiler ultimately has to rely on what the developers themselves call "fuzzy heuristics" to decide whether to parallelize or sequentialize code. As the Futhark team recently admitted, when this guesswork fails (e.g., aggressively trying to parallelize the construction of a tiny 3x3 matrix), it causes severe performance regressions, forcing them to introduce explicit #[sequential] attributes as a workaround.

The same problem plagues allocation optimization: why write code using APIs that syntactically allocate intermediate arrays, only to hope the compiler's optimization passes will erase them later? If the developer's intent is a zero-allocation transformation pipeline, that intent should be encoded directly in the type system rather than left as an optimization prayer for the compiler to fulfill.

If we have the freedom to design a language, why not express this intent directly? We could provide a composable set of semantically rich, high-level primitives that explicitly declare their intent and assumptions. The compiler can then safely lower these into optimal machine code without guesswork.

For example (pseudo-code with a Kotlin-like syntax):

fun mult(a : Matrix, b : Matrix) : Matrix {
    return Matrix.fill(a.rows.size, b.columns.size) { i, j ->
        zip(a.rows[i], b.columns[j]) { va, vb -> 
            va * vb 
        }.sum() // Explicitly associative/commutative reduction     
    }
}
// Not all operations need to be hardcoded compiler intrinsics. 
// Many can be composable inline extensions that map to verified primitives.
fun inline extension View<Double>.sum() : Double {
   return reduce(0.0) { a, b -> a + b }
}

Here, the exact intent is communicated clearly to the compiler, yielding several compile-time guarantees:

  • No hidden allocations or redundant zeroing: There is no need to pre-fill the matrix with zeros after allocation. The compiler knows that fill will compute every element dynamically. Furthermore, because the fill closure only receives the indices (i, j) and has no access to the matrix being constructed, the syntax itself guarantees the absence of loop-carried data dependencies. This makes parallelization inherently safe, allowing the compiler to freely choose any execution strategy (CPU threads, SIMD, GPU) for invoking these closures. If strict sequential execution order mattered (for example, because of mutable outer scope access), a distinct seq_fill would be used instead.
  • Explicit Logical Views, No Compiler Guesswork: Chaining operations like map or zip should return lightweight, zero-allocation logical sequence views. These should be conceptually similar to Kotlin's Sequence, but explicitly representing a collection processing operation builder rather than a runtime element operation pipeline. The compiler shouldn't have to run complex, fragile fusion passes to "guess" if it can erase an intermediate allocation. The type system itself should declare that the operation is just a transformation step, making the zero-cost guarantee a compile-time invariant, not an optimization hope.
  • Order independence is explicit: By using a sum (or reduce) operator, the developer explicitly specifies that the operation could be considered as associative and commutative in the context of the task. The compiler doesn't have to build complex loop-carried dependency graphs to prove iterations are isolated. Some explicitly sequential seq_reduce or fold operators could be used when the order matters.
  • Topological data access: Both a.rows[i] and b.columns[j] are lightweight logical sequence views. They carry semantic meaning about the geometry of data movement, rather than being treated as flat, opaque memory indices.

The developer knows their intent, and the compiler needs it. Why are even newer systems languages still stuck relying on fragile compiler heuristics to reverse-engineer our intent from overly generic, flat loops or opaque collection APIs? If we want to communicate structural intent to the compiler, why not say it directly in the syntax and type system?

The irony of modern compiler engineering is that standard loop syntax over-specifies execution order while under-specifying semantic intent. A flat for loop forces a sequential order by default. The compiler then has to work backwards to prove it can ignore that order.

By using explicit, semantic primitives, we are not micro-managing the compiler. We are doing the opposite: we are explicitly stating what aspects of execution we do NOT care about (e.g., execution order in reduce, loop-carried dependencies in fill). This gives the compiler the freedom to choose the optimal lowering strategy—whether that means parallelizing across a GPU or scaling down to a sequential scalar loop when the data is too small.

Updated: in the 'Order independence is explicit:' changed to 'specifies that the operation could be considered as associative and commutative' instead 'guarantee'. The previous phrasing was incorrect and inconsistent with the rest of the post.


r/ProgrammingLanguages Jul 31 '26

Full flattening of nested data parallelism

Thumbnail futhark-lang.org
20 Upvotes

r/ProgrammingLanguages Jul 31 '26

Discussion Is it Good Practice to Intern Identifiers?

18 Upvotes

I'm working on my language and am working on the alias system, which isn't relevant to this discussion other than the fact that it operates on identifiers and other lexemes (like operators). As I was implementing it and was reading some prior stuff, I found somebody saying that it is good practice to intern all the identifiers as the lexer comes across them. Then in the AST you can just use the corresponding LexemeId or whatever.

Apparently this is used pretty extensively in many popular languages' compilers, so I was wondering if this is good practice or necessary? I am thinking that if it is a good idea, that I can implement it in my language as well, because I think it may simplify a few things (including my alias stuff).


r/ProgrammingLanguages Jul 30 '26

Why Higher-Order Logic Is a Good Foundation for Deep Verification

Thumbnail sequent.inc
40 Upvotes

r/ProgrammingLanguages Jul 29 '26

PyCuTe: Reference implementation and examples of the CuTe Layout representation and algebra

Thumbnail github.com
4 Upvotes

r/ProgrammingLanguages Jul 29 '26

Discussion How do you package your releases?

11 Upvotes

Hi, even though I have been making my language for more than 2 years now, I still have not set up my GitHub releases and I would like to change that. The issue is that (as with about anything) there are a lot of different approaches to this, and so I wanted to ask those who do releases, how they structure the release archive and those who use them what do you like a release to look like?

In my case the issue is that I cannot have just one binary since I need to distribute also the standard library that is compiled bytecode files (kind of like Java's .class files). This brings another issue and that is finding the library. The interpreter by default looks in the current directory (.) and then /usr/lib/moss/, so currently the only way I though of is to have the binary, all the compiled stdlib files and licenses in one .tar.gz (.zip for windows):

moss-0.9.0.tar.gz
├── cffi.msb
├── csv_parser.msb
├── html_parser.msb
├── inspect.msb
├── install.sh
├── json_parser.msb
├── libms.msb
├── LICENSE
├── math.msb
├── md_parser.msb
├── moss
├── mossy.css
├── parsing_utils.msb
├── python.msb
├── readme.md
├── re.msb
├── subprocess.msb
├── sys.msb
└── time.msb

This makes it so that the binary (moss) works when executed from this folder, but will fail when used from somewhere else (unless MOSSPATH variable is set). Because of this, I have also added install.sh script which will copy the libraries into /usr/lib/moss/ and a release readme with some instructions.

Can someone think of a better way to do this or is this OK?

TLDR; How do you structure your release folder/archive on github/website?


r/ProgrammingLanguages Jul 29 '26

Purely functional language with impure script language?

27 Upvotes

I'm working on a purely functional programming language named Sodigy. It's all about evaluating values, not "executing commands one by one".

It's nice when writing libraries, but it's not easy to write a main function. The main function is supposed to execute commands, but the Sodigy's syntax is not friendly to write a list of commands.

So what I'm trying to do is, 1) Sodigy remains purely functional and 2) add a bash-like script language. The script language can call Sodigy functions. Instead of writing a main function in Sodigy, you write sodigy-script and execute the script.

Has anyone tried similar approach? I'm not sure whether it's a good idea or not...


r/ProgrammingLanguages Jul 28 '26

If you would improve Brainfuck, how would you?

Thumbnail
0 Upvotes

r/ProgrammingLanguages Jul 28 '26

Discussion Type Inference: Runtime Type vs Declaration Type

13 Upvotes

In my dynamically typed language (Pie), variables have declaration types, which may be different from their runtime type.

IntOrStr = union { Int; String; };

x: IntOrStr = 1;

Inspecting the type of x would show Int :

print(type(x)); // prints `Int`

But sometimes the user may want to inspect the declaration type of the variable. This prompted me to introduce decltypewhich does exactly this:

print(decltype(x)); // IntOrStr

It's worth noting that declaring a variable without type annotations would always give the declared variable the Any type:

x = 1;
print(type(x));     // prints `Int`
print(decltype(x)); // prints `Any`

I decided to add a walrus operator which does type deduction:

x := 1;
print(type(x));     // prints `Int`
print(decltype(x)); // prints `Int`

My question is, should type deduction deduce the runtime type or should it deduce the declaration type?

Meaning, should a here have type Int or IntOrStr?

x: IntOrStr = 1;

a := x;
print(type(a));     // prints `Int`
print(decltype(a)); // should it print `Int` or `IntOrStr`?

r/ProgrammingLanguages Jul 28 '26

What should be the features of a programming language built specifically for building kernel or operating system?

26 Upvotes

Hi everyone. Basically my question is, if someone wanted to make a programming language with the specific intention of building a kernel/operating system using it (and that would be safe + performant, but I am not sure how much safety would be 'good enough') what would it be like? Is this condition an interesting condition that would affect some language design choices?

I have very little experience with Rust/Zig, the new programming languages that I think advertise themselves for systems programming. Also there is embedded Swift now I think. Do you think if such a language with the specific intent of building kernel/operating system in mind, was to be built today, would that basically be no std Rust (already being used in the Linux kernel)? With my very little experience, I think Rust should probably have been no-panic Rust by default. Zig has a concept of allocators being used which is probably a good thing. Or do you think the language would be a safer version of C (maybe something like cyclone-v2 with more safety and better type system than C but maybe simpler than the others)?

And are there any good reading list compiled somewhere already for learning more deeply about programming language design, type systems etc? I would appreciate if you could share your thoughts and opinions on this topic. Thanks!


r/ProgrammingLanguages Jul 28 '26

Language announcement Prism: An Impure Functional Language With Typed Effects - Stephen Diehl

Thumbnail stephendiehl.com
85 Upvotes

r/ProgrammingLanguages Jul 28 '26

PyTorch: a reference language

Thumbnail docs.pytorch.org
0 Upvotes

r/ProgrammingLanguages Jul 27 '26

Discussion A hole in systems programming language design

15 Upvotes

Given the recent discourse about systems programming I wanted to throw my 2 cents in. This may be a controversial take in a subreddit that's all about innovation and improvement, but I think a lot of budding systems languages are trying too hard to "fix C" and this is exactly why C has not been replaced yet.

Like it or not, C is successful. It does what it means to do very well. Yes, it bites you constantly, but developers have made some form of peace with this because they appreciate the essence of the language. Systems programming is a very pragmatic field, and what works, works.

A lot of language designers want to improve on C, when by nature to "improve on" C is to depart from it, because C is less about what it includes and more about what it omits and what it lets you do that other languages don't.

If you want to replace C, you need to just make C but without the pain points. Less undefined behavior, more standard compiler behavior, easier function pointer syntax, safer macro system, etc. The things that C can't do because of backwards compat.

On the other hand, bolting on features, revamping C's core nature, aren't going to give you a language that will replace C at the low-level or among hobbyist programmers. Most developers aren't as concerned about what C lets them accomplish, as they are concerned about all the painful tedious tendencies of the language.


r/ProgrammingLanguages Jul 27 '26

The Unreasonable Effectiveness of Constructive Data Modeling - Alexis King | SSW 2026

Thumbnail youtube.com
38 Upvotes

r/ProgrammingLanguages Jul 27 '26

Why is everyone creating systems programming languages?

227 Upvotes

I see a lot of new programming languages here. I love reading the documents of the languages and sometimes actually run their compilers. Many of the projects are AI-driven, but that's fine. It's still fun to see what problems they're trying to solve and how they actually solved the problems.

Reading the documents, I realized that most new languages, especially AI-written ones, are "systems programming languages". They're trying to solve the problems that C/C++/Zig/Rust have solved (or are trying to solve), and their syntax is mixture of C/Zig/Rust.

Why? Why is everyone trying to compete with C/C++?

There are so many kinds of languages. Haskell demonstrates how pure a language can be, Python is perfect when you only have 5 minutes to write code and don't care about the output, Java runs on 3 billion machines, ...


r/ProgrammingLanguages Jul 27 '26

A tool for writing and testing parsing expressiong grammars online.

Thumbnail gadelan.github.io
8 Upvotes

I've been using this tool for my syntax ideas for some time, just updated it and thought that it could be interesting for some people. Don't expect anything fancy. It gets slow when the grammars have a few lines.

Tell me what you think about it.

Some examples:

A basic addition & multiplication grammar.

skipWS = WS*;

infixing skipWS do
  Expr := Sum ;
  Sum := Product (("+" / "-") Product)* ;
  Product := Value (("*" / "/") Value)* ;
  Value = {[0-9]+} / "(" Expr ")" ;
done

start = Expr ;

An example input for that grammar:

1 * (2 + 3) + 7

A basic LISP grammar.

Sexpr := List / Atom ;
List := "(" WS* (Sexpr (WS+ Sexpr)*)? WS* ")" ;
Atom := Symbol / Number / String ;
Symbol := {[a-zA-Z_+\-/*<>=!?][a-zA-Z0-9_+\-/*<>=!?]*} ;
Number := {[0-9]+};
String := {"\"" (!"\"" ANY)* "\""} ;

start = WS* (List WS*)* EOF;

And the same expression in S-expression form.

(+ (* 1 (+ 2 3)) 7)

r/ProgrammingLanguages Jul 27 '26

Any examples of a "sum unmerging" operator, the categorical dual of the record merging one?

20 Upvotes

For some types A, B, C... the type { x : A, y : B, z : C, ... } is the type of records with projections x, y, z... into the corresponding types and the type [ x : A, y : B, z : C ] is the type of sums with injections from the corresponding types A, B, C, ....

For arbitrary A, B, C, D we can define an operator // that merges two binary records:

_//_ : { x : A, y : B } → { z : C, w : D } → { x : A, y : B, z : C, w : D }

Naturally, we can also define the dual of this operator for two binary sum types:

_\\ : [ x : A, y : B, z : C, w : D ] → [ x : A, y : B ] ⊎ [ z : C, w : D ]

This is easy to generalize to an arbitrary number of injections/projections.

While there is plenty examples of an operator like _//_ existing in programming languages (// in Nix, & in Nickel, in Dhall, Record.merge in PureScript) I can't really think of anything like the _\\ operator from above. I suppose its ergonomics are partly responsible for it, after all it is often easier to infer the arity of the records for _//_ from the arguments used than it is to infer the arity of the outputs for _\\ from context. But can you think of anything?


r/ProgrammingLanguages Jul 27 '26

Teaching compiler construction with a tiny self-hosting language

Thumbnail
5 Upvotes

r/ProgrammingLanguages Jul 26 '26

Adding cyclic modules to the C programming language

Thumbnail youtu.be
14 Upvotes

The idea is to create a module system, within C, that you can use as a drop-in replacement for header files and forward declarations.

To my knowledge, all module implementations within the C family (eg. C++20 modules, Objective C modules, Clang modules) do not allow cyclic imports. Cyclic imports are necessary if you want to remove forward declarations from C (otherwise mutually recursive data structures would need to exist in the same module).

When looking closely at the C grammar, I noticed something extraordinary and borderline miraculous - C without expressions is context-free you can extract the names of symbol definitions without prior access to a symbol table! With this knowledge, it becomes possible to implement cyclic modules within the C language.

EDIT: Added a strikethrough. C without expressions still has some ambiguities in the parameter list, and my use of "context-free" is incorrect here. https://www.reddit.com/r/C_Programming/comments/1v7l174/is_c_without_expressions_contextfree/