Hacker News new | ask | show | jobs
by 59nadir 1055 days ago
There are a lot of features in Nim that are basically the polar opposite to Zig's values; macros/templates as opposed to comptime which has no real capability of just inserting random code and the very pervasive naked imports (functions/methods can come from anywhere) that are all over the place come to mind, as opposed to the explicit imports and qualified names you would have to use in Zig (or deconstruction of imports to get the bare names, making it obvious where an identifier is coming from).

On top of that you have only indirect control over memory allocation and deallocation, which goes completely against Zig's values where custom allocators are used and everything that allocates should take an allocator as an argument (or member in the case of structures). In contrast to that there isn't even the concept of an allocator in the Nim standard library.

I would say that my experience with Nim has made me fairly certain that Nim has absolutely no desire to make things obvious but rather chooses convenience over almost everything. It's not so much a competitor (in performance or clarity) to Odin or Zig as it is a competitor to Go or something with a much higher-level baseline.

On top of all of this it doesn't really have tagged unions with proper support for casing on them and getting the correct payload type-wise out of them, which is an incredibly odd choice when all of its competitors have exactly that or an equivalent.

Overall I would say that coming from Odin or Zig (or Go) and actually liking those languages it's very hard to like Nim. I could imagine that if someone came from a much higher-level language where performance is nearly inscrutable anyway and nothing is really obvious in terms of what it's doing, Nim would feel like more of the same but probably with better performance.

Edit:

Often while reading the Nim manual, news and forum posts, etc., I get the sense that Nim is really just an ongoing research project that isn't necessarily trying to solve simpler problems it already has along the way. If you look at some of the features in this announcement, it's hard to see anyone ever asking for them, yet here they are. In many ways it's way worse than Haskell, which often gets derided as "just a research language". A lot of what Nim has makes for a much worse experience learning and using the language and I'm sure it doesn't get easier in the large.

1 comments

> It's not so much a competitor (in performance or clarity) to Odin or Zig as it is a competitor to Go or something

That seems accurate. Dealing with raw pointers as one does in Odin or Zig is very much de-emphasized in favour of dealing with safe references, and a lot of effort is put into optimizing out all the overhead of those reference checks (hence ARC/ORC) and writing code to evade them. The manual memory management features of Nim are there for flexibility and fallbacks and are not really the main way to write code: even for embedded. The stuff that Zig (and Odin?) do surrounding allocators and alignment, and constructs for slightly-safer pointers, are really very interesting yet are most helpful if you are indeed working with pointers and worrying about offsets: which you usually aren't in Nim.

I am curious as to what you mean about comptime, though. I have gotten the impression that equivalent constructs in Nim are more powerful. You have `static` blocks and parameters, `const` expressions, `when` conditionals, and then also both templates and typed macros operating on the AST (before or after semantic checking)... `when` even provides for type-checking functions with varying return types (well, monomorphized to one type) via `: auto` or the `: int | bool | ...` syntax.

I will also defend "naked imports" as a feature that works very well with the rest of the language: functions are disambiguated by signature and not just name and so conflicts scarcely occur (and simply force qualification when they do). And, this allows for the use of uniform function call syntax - being able to call arbitrary functions as "methods" on their first parameter. This is incredibly useful and allows for chaining function calls via the dot operator, among other things. Besides, if you really want you can `from module import nil` and enforce full qualification.

Interest in proper structural pattern matching sparked back up again recently and some complementary RFCs were proposed: https://github.com/nim-lang/RFCs/issues/525 and https://github.com/nim-lang/RFCs/issues/527.

> I will also defend "naked imports" as a feature that works very well with the rest of the language: functions are disambiguated by signature and not just name and so conflicts scarcely occur (and simply force qualification when they do). And, this allows for the use of uniform function call syntax - being able to call arbitrary functions as "methods" on their first parameter. This is incredibly useful and allows for chaining function calls via the dot operator, among other things. Besides, if you really want you can `from module import nil` and enforce full qualification.

This is spot on. You can also not really have productive and well-fitting errors-as-values in a language that emphasizes UFCS, which is why Nim (and D) has/have to have exceptions. In order to productively use errors as values in Nim you either have to chain some kind of `Result` type (which, if you `map` & `mapError` over it will have to be able to implicitly allocate in certain cases, etc.) so the list of potential victims of this (and other features) just seems to go on and on.

In general, if you go over the list of features in Nim there is a coherence in them only in that some of the (mis)features actually have to exist in order for other features to make sense. I would feel like it was "designed" except in the case of Nim it really feels mostly accidental and not very well though out in general. The end result is (for me) that it feels very much like it ended up on the wrong side of readability, clarity and overall coherence.

> You can also not really have productive and well-fitting errors-as-values in a language that emphasizes UFCS

Eh, https://github.com/arnetheduck/nim-results and associated syntax from https://github.com/codex-storage/questionable would beg to disagree. Nim's stdlib does not have productive and well-fitting errors because it suffers from inertia and started far before the robust wonders of recoverable error handling via errors-as-types entered the mainstream with Rust and were refined with Swift (IMO). Option/Result types are fantastic and I do so wish the standard library used them: but it's nothing a (very large) wrapper couldn't provide, I suppose.

I do strongly think that other languages are greatly missing out on UFCS and I miss it dearly whenever I go to write Python or anything else. I'm not quite sure how you think UFCS would make it impossible to have good error handling? Rust also has (limited, unfortunately) UFCS and syntax around error handling does not suffer because of it. If by errors-as-values you mean Go-style error handling, I quite despise it - I think any benefits of the approach are far offset by the verbosity, quite similarly to Java's checked exceptions.

(in general concerns surrounding performance of errors surprise me - they're errors! they shouldn't be hit often! but if they are, you can certainly avoid such performance hits in nim.)