Hacker News new | ask | show | jobs
by pyinstallwoes 1274 days ago
Any die-hard lovers of Nim? Or heavy users? Why do you use it over other languages? What was your ah-ha moment?
5 comments

For me, the biggest advantage of Nim compared to other hagh-performance languages (C, C++, Rust, …) is that it doesn't overcomplicate simple things. I wrote an article about it: https://xigoi.neocities.org/nim-doesnt-get-in-the-way.html

The syntax is very pleasant to write and read, even I have some small issues with it. No unnecessary noise like braces and semicolons, no misusing the less-than and greater-than signs as brackets.

Also, it uses terminology more correctly than other languages. Procedures are procedures, not “functions”. Resizable arrays are called sequences, not “vectors”. Immutable variables are immutable variables, not “constants”.

The standard library is quite good (though it could use improvements) and extensive enough that you don't get hundreds of dependencies per project like in JavaScript or Rust.

As a Python user, I had my a-ha moment with Nim when I realized I didn't need a repl.

With Python, I used repl all the time. There's bpython, ptpython, ipython, and probably a couple more great repls because repl is really important for Python development.

Nim's INim is no match for those. But here's the great part: with Nim you don't have to test code snippets all the time. I get all my error messages before they happen. This felt liberating after Python.

Agree to disagree, I use inim all the time to test out those code snippets folks post in nim's discord to see what's broken in them :-P
I'd just save a snippet to a file and wait a second for nimsuggest to highlight the errors :-)
I only know a hand full of languages so make of that what you will. I came from a python background and hadn't learned a compiled language before, so I wanted to experiment. Tried Rust for quite a while, but found the syntax not clicking with me though a lot of ideas seemed intriguing (pattern matching and Result types). Didn't want to try C/C++ because I wasn't that hard into figuring out how memory works and at the same time was pretty shocked at how C considers types.

That only leaves go after that, which I heard about around the same time as nim. I started out with nim and I liked it enough so I stuck with it.

Why I use it over other languages? I feel like I can express myself in english readable sentences (which gives it the python vibe for me) and at the same time I have static typing, a really nice type system in general and very little chance of crap like nullpointers occurring.

My first ah-ha moment was pretty much 3 hours in when I noticed I could already already write simple stuff and only needed to consult the std lib docs here and there. The second was when I reimplemented a webserver backend that I previously had in Django (not for practical reasons, just to see how fast it could go even very little optimization) and found it performing roughly 2-5 times faster (measured by looking at request response time), despite having optimized Django's ORM to as few queries as physically possible to get the data I needed. (For reference: That's quite surprising given a decent chunk of that time is literally just network latency)

I tried Nim on a lark 3 years ago. I was writing a learn-to-code tool in Godot and was getting frustrated with some of the limitations of GDScript, so I decided to build it again in Nim. It was very much a "I don't want to do what I'm supposed to be doing right now, so instead I'll play with something shiny" moment, and I fully expected to hit a wall within a few hours and get back to doing things the normal way. But the wall didn't come, and after a few days with Nim I tossed the GDScript version.

In no particular order, things I like about Nim:

- It has most of the benefits of a scripting language, without most of the tradeoffs. Hello World is a one-liner, there isn't much syntactic noise, and it's very easy to write short, simple, useful programs that look like pseudocode, live in a single file without any dependencies, and can be kicked off with a shebang line. However, unlike scripting languages, it scales very well to large projects.

- Progressive disclosure. You can use Nim effectively while knowing very few of its features, but there's a lot of functionality available when you're ready.

- The "if it compiles, it works" factor is quite high. Not as high as Rust or Haskell, but higher than Java or Go, in my experience.

- The "it compiles on the first try" factor is also quite high. Higher than any language I've tried. "I wonder if this will work..." code usually does, the advanced features of the language mostly stay out of the way until you need them, and I rarely find myself working just to make the compiler happy. Just as an example, unless you add specific constraints, generics are "duck typed". If I pass a type to a generic proc the compiler will verify that it has the properties and functions the proc needs, but I don't have to define a specific interface up front.

- Similar to the above, the productivity vs safety balance seems right, at least for me. Code is fairly safe by default, but it's easy to work around the compiler if you need to do something it doesn't want you to do. It's also pretty easy to enforce additional safety when you need it, like ensuring a function can't throw exceptions or have side-effects.

- It's very good at building abstractions and eliminating boilerplate. Nim templates and generics are easy to use and quite powerful, and macros are there if you need something more advanced. Many features that need explicit compiler support in other languages, like async/await and string interpolation, are implemented in the Nim sdlib with macros, not the compiler.

- Nim produces standalone binaries that are both small and fast.

- The compiler is faster than most.

- The compiler can be imported as a library, making it pretty easy to write tools that understand Nim code.

- Nim programs are usually compiled, but there's also an interpreter. The compiler uses this for macro evaluation and for running build scripts, and it's easy to embed if you want your program to be scriptable.

- Nim can run on pretty much anything.

- Nim can be used to build almost anything. You can use it for systems programming, webdev (frontend and backend), games, ML, scripting, scientific computing, and basically anything else. There are definitely some domains where library support is lacking currently, but the language itself is suitable for any type of program.

- It's very flexible. Most Nim code is imperative, but it's easy to write functional, declarative, or OO style code if that's your thing.

If you prefer languages like Go that favor an abstraction-free style, where everyone's code looks more or less the same, you probably won't like Nim. However, if you want something more expressive like Ruby or Lisp, but don't want to sacrifice performance or safety, Nim is definitely worth a look.

I'm a full time Nim developer and have been for over half a decade. It's completely spoiled me for other languages, it is just ridiculously productive. Rather than an ah-ha moment, it was more a gradual transition from "where's the catch?" to "there's no catch!".

The simple answer is this: anything I want to do, I can do in Nim easier than other languages, while also having direct access to C/C++/JS ecosystems as well.

Productivity:

1. I write pseudocode, it compiles fast and runs at C speeds. Programming is fun again!

2. No `::!>><>^<<@{};` cruft everywhere. Write as you want to read, even spanning multiple lines is clean and without separators.

3. Procedural: only data types and code. No need for OOP/Trait abstractions to be forced into everything (it's there if you must).

4. UFCS and overloading by parameter types make everything naturally extendable: `len("string")` can also be written `"string".len` or `len "string"` - you don't have to remember which way round it goes, and 99% of the organisational benefit of OOP emerges from this lexing rule. A compile time error if you use the same name and parameter types, so no ambiguity.

5. Sensible defaults everywhere: type inference, stack allocated and pre-zero'd variables by default, extend with GC/manual management by type (`type HeapInt = ref int`), GC is deterministic with scope based destructors and move semantics as an optimisation rather than a straight jacket, detailed opt-in control down to assembly as you wish.

6. Arguably the best compile time support of any language.

7. AST procedural metaprogramming is a core language feature. It's hard to express how powerful and well integrated this is. You can just chuck code around and recombine it effortlessly. Whether it's simple DRY replacements, automating serialisation from types, custom DSLs at your convenience, or even generating entire frameworks at compile time from data, you effectively have another dimension to programming. I can't go back to flatland, now.

8. Flexible static typing that's as strict (`type specialId = distinct int`) or generic as you want, with concepts matching any statement against a type. You can also calculate or compose types from static values which is really nice.

9. Low overhead and high control makes it great for embedded: https://github.com/EmbeddedNim

10. Fantastic FFI that can even use C++ templates, along with loads of converters/wrappers like c2nim, futhark, pas2nim that add even more sugar to FFI interop.

Portability and glue:

- Single portable executable output.

- Compiles to C89/C99, which covers basically every piece of hardware.

- Compiles to C++ so you have C++ ABI compatibility.

- Compiles to JavaScript.

- Compiles to ObjC.

- Compiles to LLVM.

- Excellent Python interop (see: Nimpy).

- Libraries for interfacing with C# and .Net.

Thank you for your thoughtful reply. Is it fair to say that Nim and Dart share similar mission statements? Any idea of a comparison between the two?