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.
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?
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.
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.
> 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.
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.
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.
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.)
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!
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.
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.
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.
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).
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.
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.
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.
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.