It has their easy-to-learn properties, but has a better type system, the generics system and macros are arguably more useful, and more usable than what is offered in either language.
It deserves far more attention than it currently gets.
It has their easy-to-learn properties, but has a better type system, the generics system and macros are arguably more useful, and more usable than what is offered in either language.
It deserves far more attention than it currently gets.
But no, Nim is not everything "python should/want to be".
The name case insensitivity and the implicit imports on the global namespaces are perfect examples of stuff that are better in Python.
> Nim is a statically typed compiled systems programming language. It combines successful concepts from mature languages like Python, Ada and Modula.
Also, the "Features" page contains "Inspired by Python" twice, and "concepts from Ada and Modula" once.
I've been programming Python 14+ years and I've dabbled in Nim and I agree it's a very different language. Yes, there are similarities, but my expectations about it being a lot like Python from the community and docs actually led to some significant frustration and discouragement.
"Inspired by"..."borrows concepts from" are better descriptors IMO. But it would be better if an unqualified "it's like Python" disappeared.
Case insensitivity is somewhat complex topic in Nim, as there are some places where the case actually matters, plus there are some more rules for equivalence of identifiers (related to `_` IIRC). In any case, it was never a problem for me in practice; nimsuggest works quite well.
Not OP, but they are referring to `import std/strformat` (from homepage). That just imported things into the global namespace. Maybe that isn't required and its just for short examples, I don't know.
Go and Python can both import into the global namespace too (`from thing import *` and `import . somepackage`), but they are pretty frowned upon in most projects.
> That just imported things into the global namespace.
That's what tripped me up: there's definitely no such thing as importing into GLOBAL namespace. That's an import into a top-level (true) namespace of a MODULE. In Python, if you put something into the dict returned from `globals()`, you are indeed importing into global namespace, but I've never seen it actually done. Anyway, that might seem like nitpicking, but it's an important distinction. [EDIT: C `#include`s are an example of actually importing into global namespace]
With that out of the way: the wildcard imports are frowned upon for a reason. What reason? Basically, it's hard to tell a) what identifiers were imported, b) if any of them are still used in the module (ie. if it's safe to remove the import). With multiple wildcard imports in one file you also get c) hard to tell where a given identifier comes from.
Now, none of these problems apply to Nim. Nim is statically typed. The compiler and the tooling knows exactly which identifier comes from where. If you delete all the places you used identifiers from a wildcard import, the compiler and the tooling will tell you that it's safe to delete the import. Another problem with wildcard imports is that the imported modules can change, leading to disasters like silently shadowing an identifier imported from somewhere else. Again, this cannot happen in Nim - it simply won't compile.
Every single linguistic feature has its pros and cons, but you have to remember to only weigh those in the particular context they're used in. What I mean is that wildcard imports in Python and Nim are very, very different beasts, and shouldn't be directly compared, if at all. It would make more sense to compare Nim to Elixir or Scala in this regard.
Note: obviously, Nim also has selective imports and whole module imports, too.
Nim having selective imports solves any annoyances I would have.
But you mentioned all the reasons it was ok in Nim. But the answer is always that the compiler knows. That isn’t why I personally dislike this type of import. I dislike it as a user. I find it super annoying not knowing where an identifier is coming from.
I love having ‘fmt.Printf()’ instead of ‘Printf()’. Trivial example I know. I’m sure everything becomes fine once you are familiar with the ecosystem though.
split("a string", " ")
is exactly the same thing as this: "a string".split(" ")
`split` here is a normal function, not any kind of method. This means that you can chain normal functions as if they were methods on objects: "a string".split(" ").join("\n")
now, if you imported modules instead of functions themselves, how would that look like? sequtils.join(strutils.split("a string", " ") "\n")
or, if you wanted to keep uniform call syntax, you'd have to invent some new syntax: "a string".split<strutils>(" ").join<sequtils>("\n")
Which also doesn't look very appealing.So, there are trade-offs here, and you obviously might not like the choices made by Nim, which is fine! But, there are reasons for the design decisions made by the language implementers, and they are almost never predicated on a single feature: all the features of a language interact with each other, sometimes in very strange and unpredictable ways. So it makes sense to consider the features your interested in the proper context :)
[1] `import modname` and `from modname import fun1, fun2`
[2] https://en.wikipedia.org/wiki/Uniform_Function_Call_Syntax
Thanks for giving the insight.
This just feels like gatekeeping. I like nim, I've used it, talked to Dom and the author a few times in chat, filed some small bug reports. But even if I hadn't, I'd still hate the case insensitivity; I'm an experienced developer and I don't need to have used a language to know I will like or dislike some aspect of it.
In my opinion, this is the correct default, at least for Nim. Operator overloading and UFCS (https://en.wikipedia.org/wiki/Uniform_Function_Call_Syntax) don't really work if I have to prefix everything with a module name, and the static nature of Nim means that I'll get a compiler error if there's any ambiguity. There are downsides as well, but language design is all about tradeoffs, and I think Nim got this one right.
Regarding the case insensitivity, I was initially put off by this as well, but in 2 years of using Nim as my primary language, I have never, ever, encountered a real-life issue with it. I've never seen a Nim code base that uses mixed casing, and never encountered or heard of a bug caused by this behaviour. However, it has allowed my own code bases to stay 100% consistent, regardless of the code style of my dependencies, even when those dependencies written in other languages. Contrast this with Python where the `logging` module uses different casing than everything else, so you're forced to use an inconsistent style if you want to consume it. This type of thing is a non-issue in Nim. I think a case sensitive Nim would still be a fine language, but in my experience the pros of being mostly insensitive outweigh the cons.
In the end you may still disagree with both of these decisions, which is fine. Just understand that it's a nuanced discussion, and there's some solid reasoning behind their choices.
1) The compiler gives you an error, this would be my preferred solution though I'm not aware of any language that does that.
2) These end up referring to the same variable with no error. Nim's solution.
3) These are actually different variables with no error. The common solution.
Again, I would prefer (1) but I think that (2) is a safer way of handling the situation than (3).
FWIW, I do think that the late 1960s saw a broad "new prog lang" evolution away from case insensitive (often ALL CAPS due to low resolution dot matrix printers/display devices) stylistic tendencies in, e.g. LISP and FORTRAN, towards case sensitivity and lowercase. So, I suspect this was the salient driver - "We have lowercase now/text is more legible! Let's use it!". (Yes, mechanical typewriters/typesetting had it for decades to centuries, but programming was not done on those, but with printers and punch cards and just starting to be on CRTs.) There are analogies with Unicode in PLs these days (though there are obviously also other motivations these days/decades with the compute world i18n).
all the time in Python.
date = new Date()
all the time in JS.
Those are just the two languages I spend the most time in, but, but I'm sure it's trivial to find numerous examples of cases carying meaning in case-sensitive languages. Especially ones which encourage camel casing.
For instance, in C#, with capital-case convention for variables, it is common to give a variable the same name as the type.
eg `public Player Player;`
ForMid = 4 # padding for middle box in px
And: FormId = 3 # ID of the form in the user process
Should definitely not be resolved as the same variable.It isn’t the default, though.
If nim picks up pace in a few years and I already have a sense of it, it may be a good for prospects too.
You can learn that with Nim too. It supports manual memory management like C. The new ARC/ORC GC also works more like modern C++ than a traditional GC.
> its wasm support would integrate with your JS knowledge
Nim can also compile to JS. Or you could compile it first to C and then compile that to wasm, although that's not supported out of the box.
The documentation is good enough that I don't need to google most of the stuff while writing Nim, having types definitely help with that.
It does have an easier on-ramp story for getting started/adding dependencies and that may be important for getting started.
It is, but if you want to write C or C++ that doesn't crash, or just silently work incorrectly then you have to internalise these rules anyway. And having a compiler that gives you a helpful error message when you get it wrong is much easier than getting a segfault at runtime that may not even occur in a proximate part of the code. Let me put it this way: in 5 years of using Rust I am yet to need a debugger, and only need even println debugging rarely.
I don’t disagree with you on the safety aspects* and 100% real production code should really be starting in Rust. From a learning perspective though… not as sold yet. There’s a lot of things nicer (the stdlib is 100x better and more ergonomic and more pythonic in terms of “batteries included”). There’s a different set of edges though and none of the ownership concepts you learn really transfer anywhere else so you’re learning Rust’isms but not general systems programming things (just like goroutines teach you Go’isms and not what high performance thread safety means).
* The debugger claim feels specious because segfaults aren’t the only source of bugs that need debugging. How do you deal with an unexpected panic or logic bugs?
Rust is easier than C/C++, but not at first. After you learn the semantics of it, is very smooth sailing (at least until you get into the most exotic needs like build your own async runtime).
One advantage over C++ is that you don't need to learn make, IDEs or a complex build system. The nim compiler and the nimble package manager just takes care of building for you. "nim c main.nim" and off you go, ending up with a single executable, no matter how many imports, modules, etc you have.
I do recommend this as a gentle introduction: https://nim-by-example.github.io/ and recommend you use VS Code with the Nim extension by saem.