back
155 comments
Having fun with this.

Never bought into rust (have studied, have a (mostly AI-generated app in rust).

Wrote some Zig but Odin is even less overhead for me. I first loved Zigs built-in build system but having tried to wrap/use C libraries from both, I must say I prefer Odin. Wrapping some sqlite 3 API’s for my first little Odin program - just because I need so little of the API that it seems easier this way - and speaking to C from Odin is a pleasure.

That is, imho, where Rust fails the most - the second part is the C++’ish approach to memory management (RAII) - that’s not how systems programming or games (I’m told) tend to work.

To each their own. I had some fun with Rust too, but for me, Odin seems the most appealing :)

Did you try Swift? Its interoperability with C (and even C++) is great IMHO.
It's GC
Pedantically I’ll say it’s reference counted, and someone else will say that’s still a form of GC and I’ll just save us the mini-thread.

Reference counting has deterministic timing, you can run a deconstructor without registering objects for deletion and running any known finalizers (what you need to do in all GC langs I’m aware of.)

> Reference counting has deterministic timing

Define “deterministic timing”.

- One object going out of scope may mean calling free once, but it also can trigger calling free billions of objects, even for the exact same object

- Even freeing one object can have largely varying running time, e.g. to coalesce free blocks or, because it happens to be the last block in a virtual memory region, to unmap a block of virtual memory, blocking the program potentially for an arbitrary time

- With garbage collection (as with reference counting), lots of the overhead of objects going out of scope can be moved onto a specialized thread.

> you can run a deconstructor without registering objects

Not requiring finalizes does make reference counting easier, yes. The downside is have to store the reference counts somewhere, and keep them up to date (enough)

That is, imho, where Rust fails the most - the second part is the C++’ish approach to memory management (RAII) - that’s not how systems programming or games (I’m told) tend to work.

What does this mean? Who told you that?

Casey Muratori and Jon Blow have pushed this concept frequently.

They largely don't deeply elaborate, which is sad because I am a professional game developer who is interested in precisely presented knowledge so I can apply it to my work.

My interpretation is that games want to allocate large pools of resources, like GPU buffers, cpu memory, etc, and reuse that memory over and over. Ie: Reinitialize it.

In my experience, RAII is my preferred pattern for certain things (std::lock_guard), and you can almost certainly express the majority of these "uber game dev patterns" using RAII/smart pointers/etc, but the c++ implementation of these "uber game dev patterns" tends to be more complicated (and imo esthetically ugly) compared to really well written C.

These new languages (zig, odin, jai) appear to be attempts to improve C to allow an alternative to C++ that doesn't have the ugly baggage that C++ has.

From what I understood, their critique of RAII is twofold: coupling of allocation and initialisation, and enforcement of deallocation. The ease of use of smart pointers makes it tempting to allocate/free of temporary structures even within one single function. Given enough number of such occurrences, it kills performance by a thousand cuts. Also I remember they mentioned it’s not necessary to free memory if you’re about to close your program, because the OS will take the memory back. Obviously you need to gracefully deinitialise some things, like audio or other devices, but that’s beyond the discussion.

As on some references, Ryan Fleury did an episode on Wookash podcast on RAD debugger showing ECS like approach.

IMO, their RAII critique is a but nuanced, but because of their personality the discourse often gets polarising.

Edit: the sibling comment just proved my last point.

Casey Muratori and Jon Blow are both hyper-dogmatic "my way or the highway" types, and, crucially, neither of them has built any of the super high fidelity types of game that would require that level of optimisation. They're basically influencer types.
They might've pushed it, but they certainly didn't invent the idea. Although I'm not 100% familiar with their arguments.

POD - objects with no behavior are pretty popular way of representing data, you can serialize and deserialize them, etc. They have a pretty big caveat in non-GC languages - if you have an object with something like a dynamic array in it, suddenly your stateless POD becomes stateful, and needs RAII otherwise you get a memory leak. No idea how they solve that without GC.

I think those guys both hate C++ so much that they want to dismiss everything about it instead of using all the features that work for them like most people.

My interpretation is that games want to allocate large pools of resources, like GPU buffers, cpu memory, etc, and reuse that memory over and over. Ie: Reinitialize it.

They might say this, but there isn't a good technical rationalization here since anyone can create a global data structure just as easily in C++.

> Casey Muratori and Jon Blow have pushed this concept frequently

Ah... The school of what I like to call "maximum opinions and minimal evidence". Aggressive arrogant dismissal of anything except their exact view (and for Muratori, you're also "woke" for good measure), coupled with a complete lack of _hard evidence_ to back up their views. In that regard, they're not unlike "investment advice" instagram influencers.

Casey spends a lot of effort on what he considers an anti-pattern where you're making a huge number of separate objects. I think a lot of this comes from Java, a language where all the user defined types actually are obliged to be heap allocations. If I make a Goose type, and I say I want a Goose, Java will allocate space on the heap for the Goose and put my Goose there, that's really how Java works. If I make a growable array of them ArrayList<Goose>, and add each of 500 geese, that's 500 allocations for geese plus maybe 8 allocations for the ArrayList, so 508 total. Ouch. If making a Goose was itself cheap this overhead hurts badly.

But a lot of languages aren't like that, and so this doesn't translate. Obviously in Rust with Vec<Goose> that's only 8 allocations, each local Goose lives in the stack - or, if you knew up front there were 500 geese, you Vec::with_capacity(500) and it's a single allocation - but similar is true in many languages, Java is an outlier.

Games ? Many have talked about it, but many also make their games work inside of Unity and so on, so, depends on the project.

My angle is systems programming, and there, it absolutely matter. If you are performance sensitive, then you try to avoid crossing the user-space -> kernel boundary more than you have to.

Eg, ask for lots of memory, manage with arenas.

Interestingly Odin and Zig both lean into this heavily. Rust went a different route but has tried later to bolt on pluggable allocators.

Rust barely even trying on the pluggable allocators (seems like it's never going to land, afaik "allocator-api" has been sitting proposed in nightly only since 2018) is one of the things that frustrates me (fulltime Rust eng) about the language and makes me feel like it's just a language that's been eaten by web services developers, applications level work, and/or tokio, and isn't "serious" as a systems PL really despite the verbiage.

Building a database, operating system, etc. absolutely requires fine tuned control over allocation. You can get around some of these things in Rust, but it will fight you. You'll effectively have to turn your back on the containers in std and build your own vectors, maybe even your own Box, etc.

Odin looks really appealing to me at one level, but I'd have a hard time switching to any language at this point that doesn't have a borrow checker story.

> My angle is systems programming, and there, it absolutely matter. If you are performance sensitive, then you try to avoid crossing the user-space -> kernel boundary more than you have to. Eg, ask for lots of memory, manage with arenas.

This gives the misleading impression that ordinary memory allocators are materially different from arena allocators. They aren't. Both types of allocators first ask for a big block of memory from the kernel, then dole that memory out in userspace. There's no need to cross the userspace/kernel boundary more often than you need to, especially when you consider that you can replace the standard platform allocator with whatever you want.

To wit, C doesn't emphasize arena allocation anywhere near as much as Zig et al do, and yet nobody alleges that C is somehow less suitable for systems programming than these languages. Have you considered why that is? Because, for the most part, arena allocation doesn't make a significant difference, and in the places where it actually does make a difference, you can trivially build an arena allocator on top of the standard allocator.

Yeah, that's weird. I first learned about RAII from professional game developers at gamedev.net.
If the major obstacle for adopting Rust is C interop, you may find my project CO2[1] appealing. It helps you to define C crates, `#include` C headers, while exporting a Rust API with Rust types in your crate boundary.

[1]: https://github.com/hkalbasi/co2

I've been using Odin for about 6 months now, and to be honest, it's hard to find fault with it. I've used it for STM32 microcontroller firmware, web and desktop applications, and all are performant and compile quickly.

My one issue is (and I'm fully aware it will never happen) I do wish there was some sort of first-class solution to inheritance. I've grown to love procedural programming, but some problems really are just better solved with a more OOP approach. Just because classes exist does not mean they need to be used.

But as far as a language to "get stuff done" with as few tradeoffs as possible, Odin is about as good as I can imagine a language being.

Out of curiosity:

In your opinion, could a minimal system to develop in Odin be squeezed into a device like the one(s) you targeted?

That's assuming maybe some tweaks to the toolset, doing without some niceties, but not cutting core features out of the language.

Asking 'cause I have a passing interest in programming languages that allow for native development on really small implementations (think sub-1MB on bare metal). The list of candidates doesn't seem long.

Odin is not like JS or something where you'd need a VM or transpilation process to target an embedded system. It's just C with nicer syntax and modern data structures, there's no "squeezing" required. You just compile for the target you want to run on.

Here's a UI framework, if you scroll down you'll see it on a Raspi Pico: https://github.com/MadlyFX/Ansuz

I think you missed "native development". I'll rephrase:

Could a toolset to develop in Odin be made to run on (not just target) an STM32 microcontroller like you used?

> It's just C with nicer syntax and modern data structures

That suggests the above would (in theory) be possible for any device that's roughly in the same class as "can run a C compiler". Correct?

I'd like to hear more about Odin + STM32 MCU firmware, do you have any good resources? I'm also curious how difficult it may be to using it with ESP32 (ESP-IDF) / RP2350.
You can look at my repo here: https://github.com/MadlyFX/odin-embedded-boilerplate

I don't believe Xtensa (ESP32) is supported yet, but people have been asking for it, so it may happen at some point. ARM is well supported now though, obviously.

I’d be interested in reading about your experience building web applications with it. Last I looked, its stdlib didn’t have great support for that.
There’s an official http package coming out and native tls support as well.
Yeah, but OP has built web applications without those. So, I wonder how they went about it.
Personally, I have a certain Turbo Pascal feeling while playing around with it, yep I am aware of the influences.
This was a pretty funny video for a language launch:

https://www.youtube.com/watch?v=dLPAqXi9In0

I wonder if this will help them get a wikipedia page. (not sure whether this comment is a joke or serious...)
It would, but the book has existed for a while now, which is a good thing for anyone looking to learn Odin. The author, Karl, has had time to polish and update the text. He has his own Discord and is also available in the Odin Discord and makes helpful posts there as well.
Kinda wanna know why I should learn this language. Unfortunately there’s no wiki entry (deleted with some controversy abut notability), so it’s hard to get the gist of it.
Odin overview is great resource for getting a quick gist (and learning the language as a bonus): https://odin-lang.org/docs/overview/

But mainly, language doesn't have a special gimmick. The main idea (imo) is it is opinionated to have defaults to cater for majority of the cases. So once you get used to it, it is pleasure to write C-like code in it.

I wonder when we'll see new languages created specifically with LLM's in mind.
I think it would be better to approach it from the other side, the priority is not to design a language for LLMs but a language more suitable for humans to think with. And not a natural language like English, which is inconsistent and allows illogical formulations, but something like Esperanto or Interlingua (Latino sine flexione). Something that is based on mathematics and logic at the bottom, like Lean, with enough abstraction layers for a person to be able to "speak" with the machine intuitively.
The way forward will be more focused on formal specifications kind of approach, and there are already a few attempts at it.
We’ve seen several already, just search it.
What would that look like?
An LLM-only oriented language doesn't make sense, because without human generated training data there is nothing for the model to learn from.

But if a human-oriented language were to be designed to also be better for LLMs, I think it would involve deeply expressive syntax that can succinctly but distinctly represent a very broad set of common operations. Succinct so context can be managed well, distinct so completions don't confuse one thing for another, broad so as much "reasoning" can be taken away from the LLM as possible. An anti-C. A new take on the goals of Java and Go to be languages that protect the application from the Junior Developers You're Likely To Hire.

I somewhat think it would also involve application state images ala Smalltalk. LLMs seem okay at generating small deltas. Many deltas sequenced together invites compounding error. LLM generated apps are unlikely to lead to common libraries being compentized and extracted out of the application to share with other applications; it seems like LLM code generation is already a "married to a specific project" act already. So, having a living state image might reveal some benefits by leaning into incrementally developing the application in situ, as a whole.

I would guess a good way to implement a language meant for LLMs would be to strip out as many features as possible, so that all programs needed to be composed of just a handful of common structures written in shorthand. As much compiletime enforcement as possible. Maybe human-crafted abstractions for more error-prone concepts like multithreading that limit how they can be used in the language but provide ample, testable bumpers.

It would be interesting to design some mechanism where an external actor could program extensions into the language easily, which then would become a new black-box tool that an LLM could program with. Ideally though, I think you want to remove as much agency in reimplementation and re-abstraction as possible from the LLM. This is really just some tier above current high level languages, with as much dumbing down as possible.

I'm not sure this is really necessary or would even be effective because there wouldn't be a million examples for the training data of an LLM. But I imagine something along these lines could make an LLM more productive and token efficient.

I don't know but I would imagine there are a lot of inefficiencies in modern languages from an LLM perspective that it could strip out, reduce token costs, improve speed etc.
So, assembly?
Promoting here on HN is not usually allowed. This is a paid tutorial for some obscure programming language.
I have no connection with the book author. Also, people post all the time links to paid sources on HN (like books, software or pay for services).