Hacker News new | past | comments | ask | show | jobs | submit login
Introduction to ARC/ORC in Nim (nim-lang.org)
104 points by Tiberium on Oct 15, 2020 | hide | past | favorite | 57 comments



I want to remove a couple misunderstandings that I see in the comments.

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.


What a delightful talk! And how it resonates well with Nim!

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


Congrats, Nim is really becoming more and more mature as time goes on and showing new features like these are great though they do come off a bit as "a not done language"?

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?


One of the links in the end of the article leads to the RFC which talks exactly about this, see last commit by Araq especially: https://github.com/nim-lang/RFCs/issues/177#issuecomment-696...

In short: yes, the current plan is to phase out other GCs over time.


Perfect, all except markAndSweep/go, thank you!


> Will the GCs (since ARC isn't really a GC) be deprecated at that time?

Any form of reference counting is definitely a GC.

http://gchandbook.org/contents.html (chapter 5)


I guess the more interesting questions is whether it happens at compile time or run-time - ARC definitely injects derefs at compile time and therefore there are no gc-"pauses" at runtime.

(Right?)


There are always pauses at runtime, when a reference reaching the count of zero starts a domino effect of references being decreased to zero.

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.


In that regard it's not too dissimilar to Rust's semantics when memory is free'ed (https://news.ycombinator.com/item?id=23362518).

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.


Ah, that's true.


if you want to collect cycles, you have to find them, and may cause pauses.


> Will the GCs (since ARC isn't really a GC) be deprecated at that 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.


Is there a good reason to invest time in Nim for someone who already knows and likes Rust?


It entirely depends on what kind of things you want to do. For example if you want to program microcontrollers with a modern language and 0 overhead over C then Nim is a great language to learn. Or if you want to write a web front-end and back-end in the same language that compiles to JS and C respectively then Nim is yet again a great language. Or if you just want to get into programming with macros to boost your productivity or make nice patterns in your code, then Nim is a great language. Or if you are just curious as to what else that is out there, then Nim is a great language.


I am more interested in having a great ecosystem and tooling. Probably I just want a better, faster and safer Java/C#/Go. All of these seem to be quite true with Rust.


Whilst there is a pretty well fleshed out stdlib, since Nim can compile to C, C++, Javascript, and even LLVM, you can use any library written for those languages/platforms. That's a huge mass of ecosystems that are natively accessible.

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.


This sounds like "wishful programming" without type safety (or needs writing types as libraries). The only place where I've seen this working well is in TypeScript and it has a massive community (and Microsoft) behind its back.

Currently I write backends in Rust and frontends in TypeScript and it works really, really well.


https://nim-lang.org/ -- says "statically typed" within the first 7 words of the main description of the language on the homepage... Most of the features happen through the type system (eg. functions are always top-level and looked up through UFCS), and it has generics and so on.


Nim is strongly, statically typed. It's very type safe.

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.


I would too love like c# ecosystem for nim but dont think that is possible especially cuz magic macro stuff. Thing i like about Nim is joy of writing and writing less and kinda started to love whitespaces, only cons i can see is there are not like 20 payed people who work full time for lang


I would say one of Nim's biggest strengths is the productivity of Python with the performance of C; it feels like scripting, but you have production ready performance.

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.


To me this is more of an argument against. Although Python was my first language, I've had my share of burnouts because of its lack of types and difficulties in refactoring big code-bases.


> I've had my share of burnouts because of its lack of types and difficulties in refactoring big code-bases.

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.


Nim has productivity, expressiveness and readability that matches Python. Not the dynamic typing.


Don't be misleaded - the only thing Nim really has from Python is the off-side rule (indentation). Nim is statically typed and compiled to native binaries


Yeah I should have stressed that it's just the productivity that matches Python, not the operation. As you say it's similarity is only skin deep with significant whitespace.

In terms of use I find it feels a lot like a better Pascal but with powerful metaprogramming.


Another reason: compile times are super fast compared to Rust and C++ even with lots of metaprogramming.


I think this is really important for application programming, and one of the things that is promising to me about Nim. Also the metaprogramming / DSL stuff helps create eg. specific DSLs for UI programming and things like that (something we've seen people like with JSX in React for example). Can do things as far as https://github.com/krux02/opengl-sandbox (OpenGL pipeline DSL) or https://github.com/yglukhov/nimsl (literally converts Nim functions to GLSL (!)).


Compile times can be even faster (like 200 milliseconds) with the TinyCC/tcc backend. (Of course, optimization of the generated machine code suffers some.)


Nim has always seemed like an incredible do-it-all language to me.

What sorts of most programs are most frequently built with it these days?


I started dabbling with chess engines written in Nim (lots of bit twiddling but also interesting concurrency challenges) and found it delightful. Genuinely the most fun I'd had with a language for years. I think the combination of native power with light, accessible syntax and a simple mental model is very promising indeed. The fact that you also get JavaScript compilation seems great, although I've not yet used it. I have tried, and will keep trying, to fall in love with Rust because I do buy into a lot of its philosophy but just never get there.


I remember fondly programming on Inferno with Limbo that had reference counted GC and a cyclic version as well - but you were forced to declare data structures as cyclic.

Nim is interesting but I’ve been more closely following Zig lately. Perhaps it’s time to dig into Nim too!


For a good example of what ARC enables, I just did a [Show HN](https://news.ycombinator.com/item?id=24790239) for a project I've been doing for programming ESP32's in Nim.

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.


Your Show HN doesn't seem to have gotten much traction, but I am ecstatic about programming the ESP8266/ESP32 in something other than C. I don't care if it's Nim or Rust or whatever, I'm just tired of programming like it's the 80s.

I'll be watching your project with great interest.


Thanks! I was hoping it'd coincide enough but was probably too late. Maybe I'll do a write up article later and compare C/Nim/CircuitPython. It's amazing what even basic maps/tables with generics provide. No more `void ` blackholes.

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?


I'm working on them for side projects, which is why it's important for me to minimize the boilerplate I have to write. Since I usually just want to make something that works reasonably well, I usually reach for MicroPython, unless I need a library that isn't available (and I can't write it quickly).

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.


Thanks, that's good to know! I'm mostly doing Nesper for myself, partly as a way to learn the ESP/FreeRTOS libraries as I'm more familiar with the Arduino ones. However, I'd like to make it more approachable by adding some docs. One nice thing about Nim is that it provides much nicer API mechanisms that are easier to comprehend and document. Though the ESP-IDF docs are pretty comprehensive.


Hmm, I'll play around with them, thanks! I thought they'd be arcane and obscure so I never tried.


They can be a bit arcane for in parts (like EventGroups, etc), but mostly they're straightforward enough to work through. ESP-IDF actually provides better documentation of various FreeRTOS functions than the FreeRTOS docs though! P.S. I'm testing other parts of the Nesper API, and some are giving me trouble, so I'll try and start posting their on-board testing status.


Why not using something like https://github.com/Ravenbrook/mps?


What are the advantages of MPS (Memory Pool System) to ORC to your understanding?


Well, ARC/ORC is a reference-counting concept for one part and also a new implementation for Nim. MPS is a mature and proven implementation of a whole memory management infrastructure which offers different GC strategies (M&S, Copying, Incremental, etc.). And it's written in C, so it could well fit with the Nim runtime. Here is a paper: https://www.ravenbrook.com/project/mps/doc/2002-01-30/ismm20.... MPS is surprisingly unknown. I also only discovered it recently and currently try to use it in a project.

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.


Great article! I will try orc for my project.


Is Nim a systems programming language ? can it operate at the same level as rust and zig ?


Absolutely yes. The default GC (not ARC/ORC) is not stop-the-world.

"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."


Nim compiles to C, so you can use it as a "better C" like rust or Zig. That said, the main difference is that it comes with a GC, or more accurately, multiple GC options that are tunable. If you cannot have GC, as in embedded, you can compile without GC, but then you lose access to the vast majority of the standard library (which is similar to rust's --no-std, from my understanding).


You can use ARC/ORC just fine on embedded :) Not sure why do you think that it's impossible.


It used to be the case that you turned the GC off when running on embedded, but you are correct that ARC/ORC runs on embedded devices and will indeed make these even easier to program against in the future. Whether running things that normally require a GC on a tiny microcontroller is a good idea is of course entirely up to the programmer.



Many people call Go a systems language, which by my reasoning means almost anything is.

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.


Interestingly, they claim yes – the homepage says this:

> 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?


> the tradeoffs of ORC and availability of manual memory management

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.


Thank you! I'm not familiar with Nim beyond a few tutorials/docs, so this is very useful context.


Even if ORC becomes the default no one stops you from using ARC which is 100% deterministic and hard-realtime.

ORC would be enough for most normal usages, and ARC is a switch away.


The first phrase in Nim's home page answers that question... "Nim is a statically typed compiled systems programming language."

also: "Support for various backends: it compiles to C, C++ or JavaScript so that Nim can be used for all backend and frontend needs."


Not really. As the intro of this article says, it's possible to disable GC, but you loose most of the libraries, which is one of the strengths of Nim.


It can, ARC is really not a full-blown GC (people usually associate GC with a tracing garbage collector which ARC is not). ARC is fully suitable for writing any low-level stuff such as OSes and similar.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: