Introduction to ARC/ORC in Nim
nim-lang.org
nim-lang.org
Nim is "not done" and I hope it never will be. Watch Guy Steele's "Growing a Language" talk for my rationale. We want a language that can continue to evolve and introduce or research new programming language technology.
ARC is scope-based memory management computed at compile-time. It is deterministic and not stop-the-world. If you want to influence the memory management for performance reasons, it's quite trivial to do so by manipulating scopes. There are also knobs and levers if you need direct control of individual allocations.
ORC is ARC but with the tiny addition of a cycle-breaker.
ORC will be the default because it's more convenient than ARC while capturing all of the benefit. It simply adds calls to the cycle-breaker that you may be uninterested in typing in yourself.
I went first for the transcript [2] but you should go for the video [1].
[1] https://www.youtube.com/watch?v=_ahvzDzKdB0
[2] https://www.cs.virginia.edu/~evans/cs655/readings/steele.pdf
delightful: full of good things that give joy
resonates: sounds in a way that is like the one you also say, and if you hear both you get more joy
transcript: a way to track down stuff that you can read and you do not need to see or hear
What is the plan for ARC/ORC stability and possible default? Are we looking at 1.6 or later (1.8?). Will the GCs (since ARC isn't really a GC) be deprecated at that time?
In short: yes, the current plan is to phase out other GCs over time.
Not sure why you don't consider ARC a gc - it is, with the caveat that it doesn't break cycles on its own. If you have cycles you aren't going to break on your own, use ORC.
Any form of reference counting is definitely a GC.
http://gchandbook.org/contents.html (chapter 5)
(Right?)
Which is why in most high performance RC, you get a tracing GC in disguise, because the actual deletion is moved into a background cleaning thread.
An example of this in production is the C++/WinRT framework for COM/UWP.
Nim's ARC does inject refs/decrs, but they're not atomic which means it's overhead can be pretty minimal. I don't know if ObjC/Swifts ARC uses atomics or locking. GTK's object system for example uses locking which makes it pretty expensive.
Nim's FFI is excellent and makes this very easy without worrying about the ABI (since you're compiling to the target languages).
There's also excellent interop with Python with embedding Python within Nim or calling Nim from Python.
Currently I write backends in Rust and frontends in TypeScript and it works really, really well.
Nim being able to compile to Javascript and, say, C means you can write your server and web client in the same language. This means you can share code between client and server which is particularly handy for having modules with type declarations imported by both for example, so you have a unified place to update them.
You also get to use Nim's strong type guarantees and metaprogramming when outputting Javascript. An example of why this is useful is generating say, RPC calls from a static JSON file that are automatically unified between server and client.
Another productivity benefit is compiling to C, C++, and Javascript means you can natively use libraries from those languages.
If you want to go deeper, check out the extremely powerful metaprogramming capabilities that rival Lisp and I would argue are much more advanced than Rust. The end result of this is very nice syntax constructions which means easy to read code, and again translates to high productivity with no performance loss.
In terms of use I find it feels a lot like a better Pascal but with powerful metaprogramming.
Nim is strongly, statically typed. Also, I agree and aside from performance it's why I started using Nim instead of Python for my gamedev hobby. Dynamic typing isn't good for large projects. Nim has great type inference though, so you get the readability of Python but with strong compile time type guarantees.
Eg;
type SpecialId = distinct int # This int is now type incompatible with other ints
proc handleId(id: SpecialId) =
# ... do something with id.What sorts of most programs are most frequently built with it these days?
Nim is interesting but I’ve been more closely following Zig lately. Perhaps it’s time to dig into Nim too!
You can use Nim on embedded devices without GC, but it eliminates many parts of the standard library, like JSON handling. The regular Nim GC's also have problems with threading, which shows itself on the ESP32's dual cores and multi-tasking.
I'll be watching your project with great interest.
That said, `Nesper` is basically stable. It'd be nice to make prettier Nim-first API wrappers. It's stable enough that I'm planning on shipping product out next couple of weeks with it.
P.S. all of the Nim runtime and a simple async http server only adds about 80kB vs the standard `smart_config` esp wifi example! Not sure even Rust matches that.
I'm curious, are you working on embedded devices for side projects or for work?
Having something higher-level but performant, like Nim (or Rust) will make my life much easier.
What's also very important to me, though, is documentation/examples, etc. I usually use the Arduino framework, so I'm not very familiar with ESP-IDF (or Nim), which makes good documentation important to me.
EDIT: maybe a reason why people didn't consider it so far was that earlier versions were only available under Sleepycat/dual license; current versions are BSD.
also: "Support for various backends: it compiles to C, C++ or JavaScript so that Nim can be used for all backend and frontend needs."
> Nim is a statically typed compiled systems programming language.
> Nim's memory management is deterministic and customizable with destructors and move semantics, inspired by C++ and Rust. It is well-suited for embedded, hard-realtime systems.
However, this article says that ORC, a non-deterministic and non-hard-realtime GC system, is likely to become the new default GC in the future.
Perhaps they found that the "systems language for hard-realtime" wasn't as compelling a value proposition as they originally thought?
Or perhaps the tradeoffs of ORC and availability of manual memory management give them confidence that it won't materially harm hard-realtime systems use-cases in practice?
ORC would be enough for most normal usages, and ARC is a switch away.
For those not familiar with the language, Nim only uses GC for `seq` (dynamically sized lists like c++ vectors), `string`, and types declared with `ref`.
Everything else is a stack allocated value type, or as noted in the parent, you can manually manage with pointers.
> won't materially harm hard-realtime systems use-cases in practice
I think this is true. I already find myself rarely using GC when writing Nim, but when you want it, it's nice yet painless.
Here someone is embedding an async messagepack/JSON RPC on an ESP32: https://forum.nim-lang.org/t/6916
Later they compare no async, and ORC with async and find that it's "only a few ms slower than the RPC calls running with ARC and no async."
While this example may not be the hardest of realtime, it does show that ORC competes with manual approaches to memory management in constrained environments.
"Nim's memory management is deterministic and customizable with destructors and move semantics, inspired by C++ and Rust. It is well-suited for embedded, hard-realtime systems."
Like many words programmers use, there appears to be no agreed upon definition. Memory management in Nim is not usually totally manual so may be not top notch for all systems type tasks. But you can probably do it if you need to given how configurable memory management it.