isofruit
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)
Contrasting that to my experience with nim, I could get going almost immediately and thus didn't get held up much with the basics, which gave me more time to dive into more interesting concepts that it provides, like templates, compile-time-evaluation (new for me as a pythonista back then), macros etc.. I still got stuck here and there, mind you, but that was for more complex question than "How do I get a config from place X without the compiler yelling at me", and the discord was very helpful for that.
The fact that nim doesn't have exactly the same stdlib defined as python (though there's a package for that)? That Araq has said that nim isn't a variation of python?
Nim does.
What you can't do is tell your IDE to interpret camel-case as snake-case if you want (or vice versa), just because this package you really need uses camel-case and you want to use snake-case.
Not being forced into other people's style choices is a really nice boon to me.
There's options to turn it off, options to turn it into a warning when you have style issues so you can make them consistent and it prevents similar names that shouldn't exist in the first place by causing compiler errors (if you have is_ok you should not have isOk in the same scope, what are you doing). It is just not a big deal, at all. And as stated, it is not hard to ensure consistency within your codebase by heeding compiler warnings/errors.
In terms of seamlessness, I can not confirm that it is exactly like having a python class, merely "fairly close". If you define a type, you can pass it to python, python can instantiate (with the typical way of Type() ) or even receive instances (if you define e.g. a constructor proc yourself). It can pass the instances back to nim to work with no problem (see e.g. quick example I whipped up: https://pastebin.com/v5wAqGxZ).
However, I have found that there are differences of a nim-type to a pythonclass:
1) In python you do not have direct access to the fields of an instance of a nim-type. You can access them via getters and setters, but you'll get runtime errors if you try to access them directly. To make that behave seamlessly you'd need to define a python wrapper class and hide stuff with @property decorators. Without such a wrapper class you could also not add a field to an instance of a nim-type like you could a normal python object.
2) An imported nim-type can not be inherited from in python (not sure if that works in C++, but at least it's a nim-python-interop limitation).
If you wanted to make a library in nim available in python that wasn't originally intended for the export, you'd likely also need to write a glue-layer in nim. The efffort there would consist mostly in copy pasting the proc signatures you want to make available and annotating them with the export-pragma, copy pasting the nim-type to make it inherit from the type needed for exporting and converting from the exported nim-type to the imported nim-type or vice-versa when jumping from python to nim or vice versa. Further, to hide the short-comings (that you can't inherit and that you don't have access to fields or can add fields) you'd need to wrap the nim-type in a python-class as described in 1).
My conclusion from what I heard from you so far and what I know of nimpy would be, that nimpy is 90% of the way there, workable but not as seamless, given that exported nim types and their instances aren't functioning exactly like python classes and their instances. Further the fact you'd need to wrap them in a Python class to hide that means, in comparison, extra work to C++ (though that may balance out with if the glue layer in C++ is more involved than the one in nim, I don't know).
Given that, I'm curious what bits of C++/python interop are easier.
I'm mostly asking because my experience of nim-python interop consisted of: Compile nim module with nimpy imported. Put compiled nim module somewhere where your python can find it. write `import <Name_of_module>` in the python file Done, you now have access to the exported functions and symbols defined in the nim module.
Is it that C++ wouldn't need any additional imports to make it work that makes it more seamless?
Given that, I highly doubt any of them are even aware of languages beyond python and java and maybe they're aware that C and C++ exists but are scary.
I have a webpage-backend life in it, re-coded a very CRUD-heavy and medium-sized Django project in nim for funsies. You do notice that the ecosystem is not mature. You may have read another comment on here earlier that libs often are not fully featured or have clunky APIs. ORMs in particular are an area where you'll find a lot of teething issues. I found myself contributing to said libs in order to fix some of that, adding another lib to deal with connection pooling for me while I was at it.
Overall though, the coding experience has been intensely enjoyable and fun for me, which is why I kept going. Pushing logic to compile time is trivial, you don't even really need the macro system to do that since there's a pragma for that, and voila, suddenly you are able to have compile-time evaluation on whether you're trying to query a Many-To-Many relationship in your database correctly, guaranteeing you that you won't face those issues ever at runtime. I love this.
And all of this was without me having done any kind of deep dive into the macro system, I was mostly using generics, templates and compile-time-procedures (which are normal procedures, but annotated with the compile-time-pragma). And said macro-system from what I've seen opens up a whole new world of possibilities.
Another fun tidbit is that python interop is effortless, in both ways, thanks to numpy. As in, it is effortless to use python libraries, as it is effortless to make python use a library written and compiled in nim.
Edit: Deployment with docker needed some figuring out first, but wasn't hard by any means. Could use more documentation though, which I'll likely add.