Even though I’m only a dozen hours in, I feel like I can already be productive with Zig without an Internet connection.
Even though I’m only a dozen hours in, I feel like I can already be productive with Zig without an Internet connection.
I loved Nim and considered it to be my second choice. But something pulled me off. Maybe the GC. I still don't understand though why Nim is not as popular as Go. I mean it has good compile times, good performance and the best part, it compiles to C.
Why these languages and not Go or <insert language here>? Simple. Rust, Nim and Zig have `extern` with a C ABI. People don't realize how big this feature is. Two way communication with C.
I was under the impression that the Nim GC was basically entirely optional. Is that not the case? They also added a bunch of other ways to get basically the same thing, did they not? I remember atomic reference counting and so on being presented as basically a drop-in way, but maybe I misunderstood.
Because some people do not consider any "reference counting" to be "garbage collection, I think "automatic memory management" a more clear term.
Regardless, you can turn off all automatic memory mgmt with --gc:none, though the stdlib won't cooperate much. --gc:arc/orc is fairly practical, though still not quite as bug free as, say, --gc:markAndSweep. Nim gives you many options for many things.
Ref counts have a reputation for being slower than mark/sweep/generational/etc AMMs, but (with a lot of compiler assistance/optimizations) they seem at least as fast (and maybe faster) in practice, at least with Nim which usually performs like C/Rust/etc.
[1] https://researcher.watson.ibm.com/researcher/files/us-bacon/...
For example, a binary tree with <65536 nodes could use 2 byte pointers and in-node space overhead might be just 4B and if you had, say, 4B floats as key-values this might be "only" 100% space overhead with 8B per node total instead of C++ STL-like 8B x 3 (parent pointers) + other extra junk overhead. (IIRC, I measured it once as 80 bytes per node in the default `std::set` impl...).
In Nim, one could probably address this kind of situation with distinct types:
type nodePtr = distinct uint16
and overloading `[]` and so on in terms of this new `nodePtr` type.Since virtual memory dereference needs no extra "deref context", global variables/closures/local context (like an ever present proc parameter) may be needed to fully convert a narrow pointer to a VM pointer (or to otherwise access data). I think that this is all do-able in Nim, the language, but the stdlib has no direct conventions.
You might be able to keep using the rest of the stdlib by swapping out the impl of newSeq or newString to take a named parameter defaulting to some global arena, but call site-specializable to whatever you want and then replacing the `string` and `seq` types which propagate this not quite hidden optional parameter, getting the nice usability/brevity of a global arena with the nice tunability of specialized allocators. A macro pragma might even make propagating the allocator handle to called procs that also allocate semi-automatic (but someone would need to get PRs in to annotate all needed stdlib procs...). I am unaware of any Nim project which has done this yet. So, there could be compiler/run-time bugs in the way or other blockades. And it may not be possible to have narrow pointer types as with my binary tree example (but this may also not be possible with Zig's conventions).
This is all really just a little color on your probably correct conclusion (and a question about how flexible Zig's pointer types can be in its stdlib convention).
EDIT: Oh, and though that Forum asker was not you, you may get better answers than from just me here by making an account there and asking on the Nim Forum.
Ref-counting cannot cope with cycles in data structures. This can be worked around by contorting your code to work around the issue, but is not what I would call an ideal situation (it creates edge cases that more natural code would be able to cope with), simply to work around limitations in the GC (I will claim it's GC, just not as good as other reachability checkers).
These techniques still seem to be called "adjusted/qualified ref counting" rather than "garbage collection". So, I believe terminological problems have muddied the waters yet again. Or else my own incomplete description did { to the extent that differs at all. :-) }
I was trying to differentiate between "garbage collection" (the memory management family of techniques) and "specific species of algorithms for GC" (where I would class "pure reference counting" as one, and various augmentations of ref-counting as several more, as well as stop-and-copy, o the easier side).
As far as "speed" vs "completeness/correctness" goes, in many (maybe even most) cases, if I have to choose between them, I tend to choose "completeness/correctness".
At work, I changed one of our cluster build pipelines from "fast" to "correct" (making it take about 16x longer to complete). Unfortunately, that was as an action highlighted in a port-mortem as one of the root causes of a cluster completely imploding.
Young ecosystem, language still in flux, doesn't have name of big company.
D compiles (DMD compiler) pretty damn fast too. Which is exactly faster to compile D or Go depends on the nod in a horse race.
Google's marketing budget.