The Odin Programming Language
odin-lang.org
odin-lang.org
It would be interesting to hear from anyone who has spent time in both about the differences in experience.
It’s a trend of “Better C than C” languages that don’t go as far as Rust or C++ in terms of either complexity (or safety) but are more opinionated and modern than C. I think Nim can squeeze into this category, too, but maybe not? Nim is confusing to me.
Jon Blow is also working on one of these, but it isn’t released yet: https://github.com/BSVino/JaiPrimer/blob/master/JaiPrimer.md
I don't see C going away anytime soon, but it's exciting to see people attempting to replace it over the long term. Fun to see where it goes!
https://mobile.twitter.com/odinlang/status/11957620628419379...
It was really fun to hang out and talk shop
Nim is also a very complex language with numerous features to it making it a lot more complicated compared to C. So for someone who might like C might not like Nim. However, I recommend everyone try out as many languages to see what they like.
Being that I am the Odin creator, I do think you should give Odin a go because it is much more focused on being a C alternative and tackling the kinds of problems it solves.
https://forum.nim-lang.org/t/5734
The objective is to make nim suitable for embedded programming and other use cases for which garbage collection is a non starter. This new —gc:arc mode is already available in the nightly builds and the benchmarks are already impressive. I believe that the plan is to make arc the default “garbage collector mode” in nim 1.2.
Custom allocators are a pleasure to use, and allow for so much control. Along with the temporary allocator, you can make Odin feel like it's a dynamic language whilst being extremely fast. Custom allocators are an under-utilitized thing in programming in general, and I hope more people release what is possible with them that is not possible with automatic memory management schemes.
Why is garbage collection considered a negative thing?
I have no experience programming low-level languages, but I do follow and try new/obscure languages for fun.
Zig, Nim and V were the few I found + tried first, but I learned about Odin and Scopes recently and found them both interesting.
Thank you guys for the response, super appreciate it.
I guess, I can understand from an abstract perspective that you can manually tune performance and optimize to a higher degree if you can control memory allocation yourself.
And for a lot of purposes where performance is imperative, like games or embedded devices it can make or break the ability of software to function properly.
But my question then is, if languages like Crystal, Nim, or D (or any other GC lang with similar speed) can operate either at/near the performance of C, why exactly do you need manual memory management?
And if you do need it, I assume many languages that cater to this audience provide some sort of symbolic annotation that allow you to manually control GC where you feel you need it, aye?
Who's to say who's right and who's wrong? Ultimately life (and the subset of life that is software development) is a massively complex strategy and tactics game with a myriad of possible playing strategies and no agreed perfect solution.
Optional manual memory management sounds great, but I'm skeptical it would work well in practice. The reason is that if the language default is GC, libraries won't be designed for manual memory management, meaning it will be hard for your manual code to interact with data structures created by non-manual parts.
That depends entirely on how you define and measure performance. If total throughput is your metric, then it's no problem - for example, Go is perfectly acceptable for web services.
Predictability of latency, however, is absolutely _not_ on par with C code. For example, 3D rendering with a GC can easily result in perceptible stuttering if care isn't taken to minimize allocations and manually trigger the GC at appropriate times.
> some sort of symbolic annotation that allow you to manually control GC
It's not that simple. D tried to sell this at one point, but it just doesn't work for large multithreaded programs and things aren't single threaded these days. Manually controlling a global GC means manually balancing, for example, one block of threads that perform lots of allocations and deallocations (and will starve if the GC doesn't run regularly) with soft real time networking code and hard real time VR rendering code. And (for example) you certainly don't want your rendering loop pausing to scan the _entire heap_ (likely multiple gigabytes) on each frame! Alternatively, in the case of Go (and depending on your particular workload) you might not appreciate the concurrent GC constantly trashing the caches.
Custom allocators and non-atomic reference counting are fantastic though.
A programmer writing managed C# in an engine like Unity will often spend a lot of programming time ensuring that the code will not allocate every frame, as the additions to the heap will eventually trigger a pause.
That said, every game and its requirements are different, and some game development might not mind that as much. A C++ engine programmer on a Nintendo Switch is in a very different situation than a hobbyist in JavaScript or a server backend programmer on a mobile game.
I still remember when doing games in Basic/Pascal/C was considered to be like Unity nowadays, real games had to be written in Assembly.
As you say, every game and its requirements are different, and many 8/16 bit games were perfectly doable in C, Pascal, Basic and eventually the community moved along, just like it happened with C vs C++ a couple of years later.
I see the use of GC enabled languages the same way, and C# belongs to those languages that also offer other means of memory allocation, not everything needs to live on the GC heap.
There is also a common misbelief that compacting GC helps make heap allocations faster than malloc. While technically true - the allocation itself is simple and fast, because it is only a pointer bump, a problem occurs immediately afterwards - this new heap memory hasn't been touched since the last GC, and it is very likely not cached. Therefore you get a cache miss immediately after the allocation (managed runtimes initialize memory on allocation for safety). Because of that, even allocating plenty of short-lived objects, which is the best case for GC, is not actually faster than a pair of malloc+free.
There are also other overheads:
* Managed runtimes typically use heap for most allocations and make stack allocation harder or not possible in all cases - e.g. it is much harder to write Java code with no heap allocations than C.
* To facilitate GC, objects need additional word or two words of memory - e.g. for mark flags or reference counts. This makes cache locality worse and increases memory consumption.
* During heap scanning, a lot of memory bandwidth is utilized. Even if GC does that concurrently and doesn't pause the app, this process has significant impact on performance.
* Tracing GC prevents rarely used parts of the heap to be swapped out.
Several companies have been selling Java, Oberon and now Go runtimes targeted to bare metal deployment on embedded scenarios.
Some of them are more than 20 years old, so apparently they might have one or two customers keeping them alive.
The hate against GC feels like the hate against high level languages on 8 and 16 bit platforms back in the day, because anyone doing "serious" stuff naturally could only consider Assembly as a viable option.
Since you mention telecommunications, I would consider network switches running Erlang a use case of embedded development.
Other examples would be the Gemalto M2M routers for messaging processing, or some of the NSN base station reporting platform.
So while it doesn't fit your scenario, it does fit other ones, this is what some in anti-GC field need to realise.
This isn't an all or nothing equation.
You either use:
- an object. Which is on the stack and is either trivial or uses destructors and is suitable for embedded due to deterministic memory management
- a pointer object. Which is a raw pointer like C *. Managed directly via raw malloc/free or Nim malloc/free. Suitable for embedded
- a reference type. Which is managed by one of Nim GC or is an error if you use gc:none
FSecure's foundry USB security key, runs bare metal Go, TamaGo.
It runs even on the smallest microcontrollers.
A reverse corollary to Russell's Teapot? So what if there is a teapot orbiting Jupiter, I can't get it so it allows me no ability to serve tea. Therefore it might as well not exist!
Just say it's not useful to you because you can't use it. No need to say it doesn't exist, even if it just doesn't exist "for you."
(As an aside, I think a lot of societal problems in the world today have roots in relativism and such -- so it's a bit of a bugbear.)
Of course you could make the argument that its mere existence and him highlighting certain aspects could have influences on other language designers... and wow am I going down a tangent spiral now...
Anyways: TL;DR reality is objective but words can have complex semantics
Fun fact: Odin has this exact same thing with `using`. https://odin-lang.org/docs/overview/#using-statement
Windows().apply {
width = 100
height = 200
}
Now, I can't help but see the following kind of code: val someStructure = ...
someStructure.x = ...
someStructure.y = ...
someStructure.name = ...
as an immediate language code smellAs far as I can tell, Odin's `using` is applicable in the same areas as Kotlin's, but I still favor Kotlin's scoped version. When I see "using foo;" in Odin, it's not quite obvious to me where the scope of applicability is.
Any reason why you didn't do something like
entity.use {
// "this" is now the entity instance
}
// back to your regular "this"
?Koltin's approach is limited to purely the same Pascal's `with` allowed.
But if you want to be clear:
{ using entity;
// the fields of "entity" are not usable in this scope
x = 123;
}
entity.x = 123; nimc --gc:none
Nim has 5 GCs to choose from, as well as being able to have none at all.Also, the arc/orc GC is shaping up and already allows you to use exclusively reference counted memory management - so, efficient and perfectly deterministic timing but still get automatic memory management. (As usual, if you introduce cycles, it becomes more complicated)
And the Nim compiler elides many ref/unref ops, as well as keeping objects thread-local, so most performance objections to ref counting don’t actually apply. (.... and you have a choice of other automatic GC modes, including “none”)
What good is a language if you can't (easily) use its libraries?
It has very different selectable GC systems - Boehm, Bacon/Dingle, reference counting, or real-time deadline mark-and-sweep, and “none”. Perhaps I forgot one. Some libraries rely on a specific GC behavior but most work with any (with the caveat that “none” requires you to manually deal with garbage).
Nim’s mark-and-sweep is suitable for embedded systems and games, unlike Java’s, and so is the ref counting one; but even if none of the GCs work for you, the fact that there’s many of them and they are mostly interchangeable means that the dependency on them is much, much weaker than you are used to (although it still exists)
I created Odin for the very same reason that I wanted a tool that made me more productive and helped me solve the problems that I actually have. Even if Odin only benefited myself, I would class that as a success, but it has been helping so many people create amazing things.
Perhaps, but a programming language which is released when “done” is essentially dead on arrival. I’d guess the people who created amazing things with Odin helped the language move forward, right? Nothing wrong with keeping something to oneself, but promising for a long time to move something into an open source model with no set date.. I’ve rarely seen that end well. Hope I’m proved wrong though!
I've actually enjoyed playing around with Beef, the dev basically took C++ and gave it the syntax and core library design of C#. Here's a page from the guide just to give an example of the look and feel: https://www.beeflang.org/docs/language-guide/memory/
Note that despite the appearance, there's no GC, no ref-counting, explicit manual memory management, and so on.
One of my favorite things about it is the way it handles memory during debug runs. (My #1 favorite thing is that it was all developed by one guy working solo - Brian Fiete, a cofounder of PopCap Games.) First, it's able to trace allocated memory during debug execution and immediately report on memory leaks, along with the location in code where the leaked memory was allocated. Second, it can guard against use-after-frees in debug by marking the memory freed, but not actually reclaiming it. Any subsequent writes/reads to that location will immediately fail with the object's allocation stack trace.
Both of these are turned off during normal builds but seem to be excellent debugging tools.
The IDE is also cool and was developed in conjunction with the language. It's not the most feature-rich application and there are some bugs as you'd expect, but it's a perfect example of dogfooding as it was written in Beef. Hot code reloading is also supported.
Note that I have zero connection with this language, other than that I really enjoy it so far, and that I reported a minor issue that is probably very specific to me and me alone, and Brian replied to me personally and we had a pleasant brief exchange.
As far as performance vs C - there's not a lot of features that incur dynamic dispatch: virtual method calls, dynamic casts (as/is keywords), and direct interface dispatch. When you use interfaces as generic constraints, those monomorph into static dispatches unless the implementing method itself is virtual.
For C-style code, however, the performance should be the same as the C equivalent in Clang. File a bug if it isn't!
And coupled with the implicit `context` system, it can very easily profile third party Odin code's allocations too!
Existing C++ tooling (sanitizers) achieve this too.
Too true. I ran into this after about 10 minutes of trying the language. Copy pasting from a GitHub issue? Better hope those invisible newlines are what you expect.
The D programming language can be used as a better C:
https://dlang.org/spec/betterc.html
and only requires the C runtime library to link to.
Odin fills like go with "special" runtime. For example dynamic arrays are implemented as compiler component in C++.
Zig has a runtime of its own now since it has added coroutines (async/await) as a core feature, but that is still extremely minimal aw well.
I'm genuinely asking; any anecdotes out there?
Odin has aided them with a huge amount of productivity and sanity of life which other languages such as C or C++ cannot offer, such as a strong and comprehensive type system, parametric polymorphism which is a pleasure to use, the implicit context system, extensive support for custom allocators, the `using` statement, the `defer` statement, and much much more.
With all of the advantages you listed, what do you think the possibility of Odin becoming more widely accepted in other fields who could benefit from using it?
I'm more on the scientific side of CFD (so CUDA more so than shaders), but always interested in improving upon the kind of orthodox C++ I usually write.
> This starts ridiculously, then it gets worse
...
> looks like a solid way to produce abysmal quality code
...
> just learn the fucking language like everybody else
...
> The trolls from r/javascript are brigading us again It seems
I would say in my experience that Odin is definitely preferable as a client language for the simple reason that it's a nicer systems programming environment than other languages, but it's not (at least right now, future developments could always change this) equipped to run native GPU code.
Things like SPIR-V and "shader ASM" open up some interesting possibilities once the custom backend is implemented, but the complexity of integrating GPU execution means making it a first-class feature it probably unlikely.
In both cases the interoperability is excellent imo.
I can't speak to Zig as much, but Odin's type system makes working with C APIs much more of a pleasure than doing so in C itself.
I'm working with language development where we're deploying a custom language built with another custom language.
>who's actually using these languages, and in what contexts?
Compare it to using C in 1970 or Ruby in 1995. It's people who have a real problem they're trying to solve for which existing tooling and widely used languages are a poor fit - as well as people who genuinely enjoy developing their own PLs that have their own ways of doing things.
>Where does the build complexity/hiring difficulty/lack of tooling tradeoff make sense
With VC money, anything is possible...
>build complexity
Fairly nil. Contemporary build systems are language agnostic, and the steps to make one that functions good enough are well known and can be thrown together in a week or so. Not to mention it's become increasingly common for "widely used" PLs to fallback on things like python scripts to handle builds.
>hiring difficulty
It's actually a fantastic way to weed people out and find good candidates. In my (limited) experience, people willing and able to learn a new language (or have a few under their belt) are generally the exact kinds of people you want to hire anyway. Code switching between languages on a daily basis is a fantastic skill, and leads to good developers and mindshare, helps create a diverse technical background.
>lack of tooling
This is a legit problem, and one that a lot of folks stumble on. But luckily building tooling is a great way to dogfood your language - and it's never been easier to integrate with various tool frameworks via standard protocols.
But that said - every company just getting off the ground lacks tooling. For language devs it's a language server, debugger, disassembler, and what have you. For your bootstrapped startup it's build systems/procedures and code review, ERPs and CRMs, etc. Building your own tooling is a part of the growth of a business, tech or otherwise, it's just that when you work on a custom language you have to build some different tools than a SaaS might.
I'll throw one more in though that's the biggest hurdle - wheel reinvention. Stuff you take for granted in any language or have great packages for are the biggest blockers to being productive. But you do the ROI math and decide if it makes sense, so hopefully your lang has good FFI.
Also worth mentioning - if you're working in an esoteric language with heavy contribution by your own team, you're probably doing it because you have a lot of work that looks nothing like putting together a CRUD app or developing a backend for your latest API for your SaaS idea.
I highly recommend you read the overview (https://odin-lang.org/docs/overview/) and many of the Odin wrappers and bindings for more information on this topic https://github.com/odin-lang/odin-libs
It's good to see you took FFI seriously and made it low friction, it's absolutely critical for younger languages.
That's not the category I'm talking about, though. C (and I believe Ruby) was a major shift from what came before. Those two are better compared to Clojure or Go: dramatically different value-propositions that can make it worth the time and effort to take a chance on a new, green technology. What I'm talking about is languages that are "like X but with improvements to Y and Z".
Algol 60 had a number of obvious deficiencies, which needed to be addressed. Both Algol 68 and CPL were designed to fix these issues, but had a level of complexity that made them impractical to implement.
Pascal, which Predates C slightly, grew out of of Wirth's much simpler (compared to Algol 68) set of changes to Algol 60.
The CPL people were having trouble with implementation, so Richards created a stripped down version (BCPL). Thompson apparently didn't have access to the full specification, but samples of it greatly informed the the design of B, which eventually begat C. Apparently the "killer-feature" of C over B at the beginning was byte-addressing, which is arguably no more or less niche than what any of these new languages claim.
Almost memory safe systems programming languages (not fully safe due to use-after-free) were already a thing since 1961.
In fact ESPOL was probably the very first systems programming language with unsafe code blocks.
In this case, small minds discuss libraries/frameworks, average minds discuss language details, great minds still discuss ideas.
All of the best, most interesting, and most insightful programmers I have talked to express ideas in language and tool-agnostic terms, with written language, and only use code as an implementation or demonstration detail. This isn't to say that tooling and languages are not important of course, but the best programmers can pick up new tools and languages quickly and express their ideas in any given medium. If a programmer is limited by thinking in terms of the language(s) they know, they're basically a bricklayer. Still respectable, but they're doing tedium rather than manipulating the idea space.
There's also something to be said for language idioms as a means of communication and shared documentation within a team. Someone who speaks French and Italian may have no trouble picking up Spanish, but might say things that lose meaning when translated literally until she's gotten more immersed in the culture surrounding the language. One of your "great minds" may pick up C# and try to use it like Clojure, or vice-versa, and create enormous friction within the codebase.
Not that this is necessarily the dominant factor in these decisions, but it is a factor, no matter how intelligent and experienced your hires are.
Yes, but it never matters at first. The first thing you do, always, is hack a prototype together. Computers are fast. Basic algorithms and data structures are usually good enough. You don't optimize until you've got what you want in principle. And sometimes you don't optimize at all. The internet is full of prototypes that escaped into the wild and then attracted enough users that they became irreplaceable.
Javascript is a good example. The version we have is several versions too early, and it's warty. JS would be a much better language if someone had paid more attention to the low-level details. But I would argue that JS is a huge success in spite of this. An awful lot of work has been done in JS, including work that couldn't have been done in most other languages.
The grandparent of this post is right that ideas are the important thing. JavaScript succeeds because it borrows good ideas from Scheme and Self.
When I say boring, I mean relatively; there is some "puzzle solving" merit to programming per se. But this is mere mechanics and minor compared to the experience of manipulating ideas into practicable forms before writing them out in code.
I wonder if a large company will throw into this royal of small and productive system languages. It seems like Nim, Zig/Odin, and presumably Jai all fit into their corners well. Someone with funds to generate tooling and combine the static structure of Zig, the productivity of coding in Nim. I think these language have all shown the potential for a great new language in this space.
One cool aspect of all of these new programming languages entering the arena is that most of them only have a minimal (or no) runtime. That should make it much easier to interoperate between all of these languages, something which has traditionally been difficult with most programming languages.
Disclosure: I'm the author of Muon [1], a low-level language that embodies similar design principles.
For me a C-replacement has to be a very simple and disciplined language, with 100% focus on low-level programming.
Nim is great though, I've used it to make some small tools/programs
Zig is also wonderful, hope to use it more once it's more stable
If you have tried Zig and/or a C or C++ programming, you might really enjoy programming in Odin. Odin feels wonderful to use and has solved all of the issues that C had and more.
I recommend reading the Overview (https://odin-lang.org/docs/overview/) and the Demo (https://github.com/odin-lang/Odin/blob/master/examples/demo/...)
P.S. Odin is now fully recognized by GitHub and supports syntax highlighting.
Go was designed to fill the need that C was originally developed for (writing Unix applications), not for what C is more commonly used for today.
IMO it does deserve a place in that list today and is definitely worth a gander.
Nim should be with D in the category 'C++ replacement with a GC by default'.
By the way, there seems to be a css bug in the FAQ/quote section (The text shows dark grey on a dark grey background)
I have fixed the css bug now.
Most of my internal projects are usually named after a mythological figure before I give it a finalized name. It just makes me stop worrying about it and get on with solving the problems at hand.
One thing that did take some time was getting System V ABI support for the Odin compiler so that nix platforms could support C-ABI 100%.
As for VCVARSALL.BAT, there is work currently trying to remove the requirement for it once the compiler is built on Windows. However, Microsoft being what it is, it's quite annoying to get it working on everyone's machine correctly.
I wonder if there is any feature that makes it unable to be translated to efficient C and therefore preventing it from bootstrapping with a C backend like Nim and V. I'll look forward to the self-hosting compiler.
Lots of niceties don't exist yet.