1
 
 
This is an automated archive made by the Lemmit Bot.

The original was posted on /r/programminglanguages by /u/othd139 on 2026-04-23 08:06:14+00:00.


Pretty much what the title says. It's not a particularly big project since I made it over the span of basically 2 days lol. Anyway, here's the github repo if anyone is interested in writing some strange and convoluted code.

2
Borrow-checking without type-checking (www.scattered-thoughts.net)
submitted 3 months ago by [B] to c/programminglanguages@lemmit.online
 
 
This is an automated archive made by the Lemmit Bot.

The original was posted on /r/programminglanguages by /u/jamiiecb on 2026-04-23 02:54:06+00:00.

3
 
 
This is an automated archive made by the Lemmit Bot.

The original was posted on /r/programminglanguages by /u/yorickpeterse on 2026-04-22 13:07:49+00:00.

4
 
 
This is an automated archive made by the Lemmit Bot.

The original was posted on /r/programminglanguages by /u/mttd on 2026-04-22 04:10:59+00:00.

5
 
 
This is an automated archive made by the Lemmit Bot.

The original was posted on /r/programminglanguages by /u/Athas on 2026-04-22 15:44:49+00:00.

6
 
 
This is an automated archive made by the Lemmit Bot.

The original was posted on /r/programminglanguages by /u/mttd on 2026-04-21 03:25:32+00:00.

7
 
 
This is an automated archive made by the Lemmit Bot.

The original was posted on /r/programminglanguages by /u/theScottyJam on 2026-04-18 04:35:36+00:00.


I love how lambda calculus (and turing machines) have very few rules to them, and yet you're able to express any program you want using them. These aren't technically programming languages (though it's not too hard to make runtimes for them), but there do exist esoteric programming languages with real runtimes that follow similar principles, the most widely known being brainf***.

Is there anything equivalent for proof assistents? A language with extremely minimal syntax/semantics that's capable of letting you both write code, and perform arbitrary proofs about that code? Of course writing proofs in such a language wouldn't be fun, just like people don't tend to write programs in Lambda Calculus, but still, I'd be interested to know if such a thing exists.


Some background that's causing me to ask this question:

I've been interested in learning about proof assistances, and maybe even building one myself. A major reason for me asking this question is because I'd like to get a better understanding of what kind of "primitive proof operations" could be used to build up a proof. I could then use them as inspiration in my own design and/or build up more complicated operations from the simpler ones, who knows, depends on what I learn from them. It's also just something I'm generally curious about, even if I don't end up using what I learn for any personal projects.

So far I've played around with the Agda language, a dependently typed language. And the idea of dependent types are pretty cool - it feels clean the way you can express your desired behavior in type signatures, then write your proofs in the function bodies, but I feel like a lot of black-magic complexity is getting hidden under their unification algorithm - it's not extremely clear how it decides if two types are actually the same or not - and for higher-level languages, perhaps that's fine - you don't need to know all the details, you just need to know if the tool accepts your proof, or if you still need to break it up into smaller steps.

But for this question, I'm hoping to find something that required the programmer to be more explicit when they write their proofs, even if it makes the language extremely annoying to use. I'm imagining it would be some kind of "I want to substitute at this location in the expression using that axiom, then I want to ..." type of thing.

Anyways, maybe such a thing doesn't exist, but any thoughts on the subject would be appreciated.

Thanks.

8
 
 
This is an automated archive made by the Lemmit Bot.

The original was posted on /r/programminglanguages by /u/mttd on 2026-04-21 15:54:52+00:00.

9
 
 
This is an automated archive made by the Lemmit Bot.

The original was posted on /r/programminglanguages by /u/marvinborner on 2026-04-20 15:40:15+00:00.

10
 
 
This is an automated archive made by the Lemmit Bot.

The original was posted on /r/programminglanguages by /u/tobega on 2026-04-21 05:41:59+00:00.


In the 1950s, programming languages rose above the level of direct machine instructions to be based on the mathematical models of computation instead.

This is still quite low-level compared to what programmers really want to achieve, which makes code harder to write and review than would be desirable. Making the connection between the code and the program logic more direct would have real economic consequences.

In this essay I take a look at that intent-to-implementation gap and some possible re-imaginings of how things could work.

https://tobega.blogspot.com/2026/04/rising-above-mechanics-of-computation.html

11
 
 
This is an automated archive made by the Lemmit Bot.

The original was posted on /r/programminglanguages by /u/fredoverflow on 2026-04-20 09:05:21+00:00.


[They] invented a language called max--, and wrote a compiler for it from scratch in C/C++.

12
 
 
This is an automated archive made by the Lemmit Bot.

The original was posted on /r/programminglanguages by /u/bzbub2 on 2026-04-16 23:18:06+00:00.


came up in my youtube, ignore the siilly ai thumbnail it has nice interviews with rich hickey and others

13
 
 
This is an automated archive made by the Lemmit Bot.

The original was posted on /r/programminglanguages by /u/swe129 on 2026-04-18 13:31:00+00:00.

14
 
 
This is an automated archive made by the Lemmit Bot.

The original was posted on /r/programminglanguages by /u/sdogruyol on 2026-04-17 15:06:14+00:00.


Crystal 1.20.0 is officially here

Crystal is a general-purpose, object-oriented programming language. With syntax inspired by Ruby, it’s a compiled language with static type-checking. Types are resolved by an advanced type inference algorithm.

Significant performance leaps and architectural improvements are now live. Here are the 3 most impactful updates in this release:

  • M:N Scheduling: A major shift in the scheduling architecture that drastically optimizes concurrency and resource handling.

  • Multi-threading Refinement: Critical improvements to parallel execution efficiency and overall system stability.

  • Broadened Platform Support: Official Linux ARM64 builds and enhanced Windows stability make Crystal production-ready across environments.

Time to update your shards!

Release Post: https://crystal-lang.org/2026/04/16/1.20.0-released/

15
 
 
This is an automated archive made by the Lemmit Bot.

The original was posted on /r/programminglanguages by /u/swe129 on 2026-04-17 13:00:02+00:00.

16
 
 
This is an automated archive made by the Lemmit Bot.

The original was posted on /r/programminglanguages by /u/RedCrafter_LP on 2026-04-14 13:24:12+00:00.


I'm currently debating how I deal with comments. The easiest way would be to just strip all comments and continue with the rest. But what do you think? How far to drag comments around? Are there uses during compilation for comments? Attach doc comments to the ast node?

17
 
 
This is an automated archive made by the Lemmit Bot.

The original was posted on /r/programminglanguages by /u/ella-hoeppner on 2026-04-15 21:58:32+00:00.


A while ago in rust I was working on some graphics stuff, and I decided to represent positions in 2d space as a tuple of two floats, (f64, f64). But then I wanted to use a library for some geometry stuff, and tragically, I found that it had chosen to use a slightly different type to represent it's points: [f64; 2], an array of two floats.

This was slightly annoying because it meant I had to insert a bunch of conversion functions when working with the library, but so be it, that kind of thing happens a lot when trying to make different libraries work together. But it got me thinking; why should these even be considered to be different types? A tuple of two floats and an array of two floats are both just ways of grouping two floats together in a way where they can later be differentiated by an index 0 or 1, so why even have two different built-in types for representing that same thing?

Obviously, in general, the main difference between tuples and arrays (and in this post I'm always referring to fixed-sized arrays, I'm not talking about "dynamic arrays" that don't include their size as part of their type) is that arrays contain only a single type of element at each of the different indices, while tuples can be heterogenous, storing different types at different indices. In this sense, tuples are more general than arrays, in the sense that for any array type you could construct a corresponding tuple type, with the same number of elements, all of the same type.

So why not make this more than an analogy, and just have your type system literally treat arrays as a special case of tuples? I don't see any downside to this. In other words, in a language like rust, why not just have an array type like [f64; 3] literally just be a type-alias for a tuple type (f64, f64, f64)?

Of course, the main thing that you can do with arrays that you can't do with tuples is index into them with a dynamic value. In rust, to get the value out of a tuple you have to use a syntax like my_tuple.0 or my_tuple.1 to get the internal values, you can't use a syntax like my_tuple[n] with some dynamically-defined n. It wouldn't usually be possible to assign a coherent type to an expression that dynamically indexes a tuple, since in general the values in a tuple aren't of the same type. But in the case where all the types in a tuple happen to be the same, a dynamic indexing expression like that absolutely could be well-typed! So rather than having a whole separate "array" type with the only difference from a tuple being that it can be dynamically indexed, you could just have a rule in the type system saying that dynamic indexing is only allowed on homogenously-typed tuples.

Do any existing languages take this approach? Are there any downsides here that I'm not thinking of? It just seems redundant and inelegant to have arrays and tuples be fundamentally different types, when they're both just fixed-sized linear collections of values. Having a nice syntax for describing array/homogenous-tuple types is definitely important, so that you can write [f64; 100] rather than the absurdly long (f64, f64, f64, ..., but ultimately it seems more elegant for this to just be shorthand for a tuple type, rather than a fully-fledged type of its own.

And just to be clear, I'm not actually suggesting that rust, specifically, should adopt this. Obviously that would break a lot of things and the rust team is not interested in making those kinds of changes at this point in the language's development. I'm just using rust syntax as an example and making a design suggestion for brand new languages, and contemplating whether I should go this route in my own language, or whether there's some downside to this that I haven't thought about.

18
 
 
This is an automated archive made by the Lemmit Bot.

The original was posted on /r/programminglanguages by /u/Jipok_ on 2026-04-15 19:26:59+00:00.


I recently read the popular So you're writing a programming language post here, and it hit me hard.

I started building a toy language (let's call it JLang for now) purely as an experiment. I wanted to see if I could implement Go-style concurrency (goroutines + channels) in a dynamically typed script without a GIL and without a massive runtime. Just wanted a zero-dependency single binary.

Things got out of hand. After implementing a few optimizations, the VM accidentally became really fast. Now I have a reliable engine, but I’m struggling to figure out its actual niche.

Here is the tech dump: * Entire code about ~2,500 lines of pure C. With PGO the completely standalone executable is just 71kb. * Single-pass Pratt parser emitting bytecode directly to a Stack-based VM. Uses NaN-tagging. * Green threads multiplexed on a lazy pool of OS threads (M:N Scheduler, pthreads). * Complex objects (Arrays, Dicts) have an atomic lock_state flag in their 8-byte header. You can explicitly lock them in scripts for batch transactions. Accessing a locked object gracefully parks the goroutine. * Communication via bounded ring buffers(channels). If a goroutine hits an empty channel, the VM simply rolls back the instruction pointer, suspends the goroutine directly into a lock-free queue (ParkingBucket), and context-switches.

* No "Stop-The-World" tracing GC. Local objects use a thread-local Bump Allocator. To avoid atomic overhead, sending an object through a channel triggers a "handoff" bypass if ref_count == 1. If an object is globally shared, it automatically upgrades to thread-safe Atomic Reference Counting (ARC) using spinlocks. No cycle collector.

I ran some microbenchmarks (IPC, context switches, L1 misses via perf) on a low-power Intel N100 against Python, Wren, Node, Lua, and Go.

(Note: Because my codebase is so small, I wrote a script to do a full build + PGO profiling in ~2 seconds. The others were standard precompiled/package-manager binaries.)

In single-threaded execution, it easily outperforms Python and Wren, and sits right neck-and-neck with Lua 5.5. I obviously can't beat LuaJIT's hand-written ASM interpreter in pure math loops, but my engine actually matches or slightly beats it in heavy hash-map and allocation workloads.

In compute-heavy or deeply recursive workloads, Go absolutely crushes my engine (often taking a fraction of the time). Static typing and AOT optimizations simply cannot be beaten by my goto VM dispatch.

However, in "natural" orchestration workloads like a highly concurrent worker pool, object pipelines, or spin-lock synchronization JLang stays remarkably close, often running within a comfortable margin of Go's execution time. In one specific microbenchmark (passing messages through a massive ring of channels), it actually finished noticeably faster than Go!


My dilemma: Language dev is O(n²), and I need to stop adding random features What's the Next Step?

  1. The "Multi-threaded Lua" (Embeddable Engine) Make it a pure C-library for game engines or C++ servers. Lua is the king of embedding, but lacks true CPU-core multithreading for a single state. This VM can run go ai_script() and distribute it safely across CPU cores. Empty standard library (no net, no fs). The host application deals with bindings.

  2. The "Micro-Go" (Standalone Scripting Tool) Make it a standalone scripting engine for concurrent networking, web scraping, and lightweight bots. Forces me to write a standard library from scratch.

  3. The "Modern Bash Replacement" (Ops/Tools) Add pipeline operators (e.g., cmd1 |> cmd2) and use the concurrency to run parallel system tasks, replacing massive and slow bash/python scripts.

  4. ???


Syntax looks like:

// Note: Complex objects (like Dicts/Arrays) are structurally thread-safe by default. 
// You only need explicit lock() to prevent logical data races!

let result\_chan = chan(10)
let num\_workers = 5000

let worker = fn(id) {
 // Heavy internal work
 // ...
 let payload = {}
 payload["worker\_id"] = id
 payload["status"] = "done"

// Safely send the complex object across threads. result_chan <- payload


}

// Spawn 5000 lightweight green threads
let w = 0
while w < num\_workers {
 go worker(w)
 w = w + 1
}

let completed = 0
while completed < num\_workers {
 let response = <-result\_chan
 print("Received from: "); print(response["worker\_id"])
 completed = completed + 1
}


Has anyone pivoted a toy language into a specific niche? Any advice on which path makes more architectural sense?

P.S. The code is currently a single chaotic 3,000-line C file. Once I decide on the architectural direction, I will decouple the scheduler/parser, write a readme, and publish the repo.

P.P.S. I don't speak English and wrote/translated this post using LLM.

19
 
 
This is an automated archive made by the Lemmit Bot.

The original was posted on /r/programminglanguages by /u/mttd on 2026-04-15 15:00:23+00:00.

20
 
 
This is an automated archive made by the Lemmit Bot.

The original was posted on /r/programminglanguages by /u/UKbeard on 2026-04-14 19:56:47+00:00.

21
 
 
This is an automated archive made by the Lemmit Bot.

The original was posted on /r/programminglanguages by /u/mttd on 2026-04-13 23:45:25+00:00.

22
 
 
This is an automated archive made by the Lemmit Bot.

The original was posted on /r/programminglanguages by /u/MarcoServetto on 2026-04-12 04:52:45+00:00.


Why Unicode strings are difficult to work with

A simple goal

This text is part of my attempts to design the standard library API for unicode strings in my new language.

Suppose we want to implement:

text removePrefixIfPresent(text,prefix):Text

The intended behavior sounds simple:

  • if text starts with prefix, remove that prefix
  • otherwise, return text unchanged

In Unicode, the deeper difficulty is that the logical behavior itself is not uniquely determined.

What exactly does it mean for one string to be a prefix of another?

And once we say "yes, it is a prefix", what exact part of the original source text should be removed?


The easy cases

Case 1/2

text text = "banana" prefix = "ban" result = "ana"

text text = "banana" prefix = "bar" result = "banana"

These examples encourage a very naive mental model:

  • a string is a sequence of characters
  • prefix checking is done left to right
  • if the first characters match, remove them

Unicode breaks this model in several different ways.


First source of difficulty: the same visible text can have different internal representations

A very common example is:

  • precomposed form: one code point for "e with acute"
  • decomposed form: e followed by a combining acute mark

Let us name them:

text E1 = [U+00E9] // precomposed e-acute E2 = [U+0065, U+0301] // e + combining acute

Those are conceptually "the same text". Now let us consider all four combinations.

Case 3A: neither side expanded

text text = [U+00E9, U+0078] // E1 + x prefix = [U+00E9] // E1 result = [U+0078]

Case 3B: both sides expanded

text text = [U+0065, U+0301, U+0078] // E2 + x prefix = [U+0065, U+0301] // E2 result = [U+0078]

Case 3C: text expanded, prefix not expanded

text text = [U+0065, U+0301, U+0078] // E2 + x prefix = [U+00E9] // E1 result = [U+0078] // do we want this result = [U+0065, U+0301, U+0078] // or this? exact-source semantics or canonical-equivalent semantics?

Case 3D: text not expanded, prefix expanded

text text = [U+00E9, U+0078] // E1 + x prefix = [U+0065, U+0301] // E2 result = [U+0078] // do we want this result = [U+00E9, U+0078] // or this?


Overall, exact-source semantics is easy but bad. Normalization-aware semantics instead is both hard and bad.

Still, the examples above are relatively tame, because the match consumes one visible "thing" on each side.

The next cases are worse.

Extra source of difficulty: plain e as prefix, "e-acute" in the text

This is interesting because now two different issues get mixed together:

  • equivalence: does plain e count as matching accented e?
  • cut boundaries: if the text uses the decomposed form, are we allowed to remove only the first code point and leave the combining mark behind?

Let us name the three pieces:

text E1 = [U+00E9] // precomposed e-acute E2 = [U+0065, U+0301] // e + combining acute E0 = [U+0065] // plain e

Case 3E: text uses the decomposed accented form

text text = [U+0065, U+0301, U+0078] // E2 + x prefix = [U+0065] // E0 result = [U+0301, U+0078] // do we want this (leave pending accent) result = [U+0065, U+0301, U+0078] // or this? (no removal)

Case 3F: text uses the single-code-point accented form

text text = [U+00E9, U+0078] // E1 + x prefix = [U+0065] // E0 result = [U+0078] // do we want this (just x) result = [U+00E9, U+0078] // or this? (no removal) result = [U+0301, U+0078] // or even this? (implicit expansion and removal) Those cases are particularly important because the result:

text [U+0301, U+0078]

starts with a combining mark. Note how all of those cases could be solved if we consider the unit of reasoning being extended grapheme clusters.


Second source of difficulty: a match may consume different numbers of extended grapheme clusters on the two sides

text S1 = [U+00DF] // ß S2 = [U+0073, U+0073] // SS

Crucially, in German, the uppercase version of S1 is S2, but S2 is composed by two extended grapheme clusters. This is not just an isolated case, and other funny things may happen, for example, the character Σ (U+03A3) can lowercase into two different forms depending on its position: σ (U+03C3) in the middle of a word, or ς (U+03C2) at the end. Again, those are conceptually "the same text" under some comparison notions (case insensitivity)

Of course if neither side is expanded or both sides are expanded, there is no problem. But what about the other cases?

Case 4A: text expanded, prefix compact

text text = [U+0073, U+0073, U+0061, U+0062, U+0063] // "SSabc" prefix = [U+00DF] // S1 result = [U+0061, U+0062, U+0063] // do we want this result = [U+0073, U+0073, U+0061, U+0062, U+0063] // or this?

Case 4B: text compact, prefix expanded

text text = [U+00DF, U+0061, U+0062, U+0063] // S1 + "abc" prefix = [U+0073, U+0073] // "SS" result = [U+0061, U+0062, U+0063] // do we want this result = [U+00DF, U+0061, U+0062, U+0063] // or this?

Here the difficulty is worse than before.

In the e-acute case, the source match still felt like one visible unit against one visible unit.

Here, the logical match may consume:

  • 2 source units on one side
  • 1 source unit on the other side

So a simple left-to-right algorithm that compares "one thing" from text with "one thing" from prefix is no longer enough.


Third source of difficulty: ligatures and similar compact forms

The same problem appears again with ligatures.

Let us name them:

text L1 = [U+FB03] // LATIN SMALL LIGATURE FFI L2 = [U+0066, U+0066, U+0069] // "ffi"

Again, those may count as "the same text" under some comparison notions.

Case 5A: text expanded, prefix compact

text text = [U+0066, U+0066, U+0069, U+006C, U+0065] // "ffile" prefix = [U+FB03] // L1 result = [U+006C, U+0065] // do we want this result = [U+0066, U+0066, U+0069, U+006C, U+0065] // or this?

Case 5B: text compact, prefix expanded

text text = [U+FB03, U+006C, U+0065] // L1 + "le" prefix = [U+0066, U+0066, U+0069] // "ffi" result = [U+006C, U+0065] // do we want this result = [U+FB03, U+006C, U+0065] // or this?

This case can also be expanded in the same way as the e-acute/e case before:

text = [U+FB03, U+006C, U+0065] // L1 + "le"
prefix = [U+0066] // "f"
result = [U+FB03, U+006C, U+0065] // no change
result = [U+0066, U+0069, U+006C, U+0065] // remove one logical f
result = [U+FB01, U+006C, U+0065] //remove one logical f and use "fi" ligature
result = [U+006C, U+0065] // remove the whole ligature


Boolean matching is easier than removal

A major trap is to think:

"If I can define startsWith, then removePrefixIfPresent is easy."

That is false, as the case of e-acute/e.

A tempting idea: "just normalize first"

A common reaction is:

  • normalize both strings
  • compare there
  • problem solved

This helps, but only partially.

What normalization helps with

It can make many pairs easier to compare:

  • precomposed vs decomposed forms
  • compact vs expanded forms
  • some compatibility-style cases

So for plain Boolean startsWith, normalization may be enough.

What normalization does not automatically solve

If the function must return a substring of the original text, we still need to know:

  • where in the original source did the normalized match end?

That is easy only if normalization keeps a clear source mapping.

Otherwise, normalization helps answer:

  • "is there a match?"

but does not fully answer:

  • "what exact source region should be removed?"

Moreover, this normalization is performance intensive and thus could be undesirable in many cases.

Several coherent semantics are possible

At this point, it is clear that any API offering a single behavior would be hiding complexity under the hood and deceive the user. This is of course an example for a large set of behaviours: startsWith, endsWith, contains, findFirst, replaceFirst, replaceAll, replaceLast etc.

So, my question for you is: What is a good API for those methods that allows the user to specific all reasonable range of behaviours while making it very clear what the intrinsic difficulties are?

23
Borrow-checking surprises (www.scattered-thoughts.net)
submitted 3 months ago by [B] to c/programminglanguages@lemmit.online
 
 
This is an automated archive made by the Lemmit Bot.

The original was posted on /r/programminglanguages by /u/jamiiecb on 2026-04-10 05:22:55+00:00.

24
 
 
This is an automated archive made by the Lemmit Bot.

The original was posted on /r/programminglanguages by /u/othd139 on 2026-04-08 23:03:31+00:00.


I've been writing an eBook on how to write a compiler using my custom backend I posted here a couple of weeks ago Libchibi (which I've made quite a few changes to as the process of writing this book has revealed flaws and bugs). I'm a fair way from done but I've reached an important milestone and I successfully wrote a pendulum simulation using raylib to render it in the language I've been developing in the book. I'd love some feedback as to the tone, teaching style, density, depth etc... if anyone feels like having a look (although I get that it's kinda long for something incomplete so I'm not expecting much). In any case the language I've been writing for it is kinda cool and at the point of being genuinely usable (although a ways from being preferable to anything out there already for serious use). Anyway, here's the repo: https://github.com/othd06/Write-Your-First-Compiler-With-Libchibi

Edit: It's just occurred to me I didn't really describe what I was going for with the eBook. I was quite inspired by Jack Crenshaw's Let's Build A Compiler if any of you are aware of that 80s/90s classic so I wanted to keep the practical, conversational tone of that but I wanted to introduce tokenisation and grammar much earlier so that I don't get stuck with a lot of the downsides that book had. So it's quite practical and building and giving enough theory to be grounded and know where you are but quickly into actually building something and seeing results.

25
1SubML: Plan vs Reality (blog.polybdenum.com)
submitted 3 months ago by [B] to c/programminglanguages@lemmit.online
 
 
This is an automated archive made by the Lemmit Bot.

The original was posted on /r/programminglanguages by /u/Uncaffeinated on 2026-04-05 00:33:33+00:00.

view more: next ›