Hacker News new | past | comments | ask | show | jobs | submit login
Nim: Scripting Ease in a Compiled Language (junglecoder.com)
209 points by charlieflowers on Oct 29, 2019 | hide | past | favorite | 166 comments

One thing I find annoying about Nim is the case insensitivity [0]. There was no strong reason for this "feature" and literally no mainstream language does it. Moreover it makes code search a pain.

[0] https://nim-lang.org/docs/manual.html#lexical-analysis-ident...

It's not just case insensitive, it's underscore insensitive.

So MyNimName, mynimname, MY_NIM_NAME, My_Nim_Name, __MYNIM___name_, and any other variation you can think of are all the same name!

Most search tools have the option of case sensitive or insensitive search, but a search that does that and ignores underscores? Not too many of those outside the Nim world.

An interesting contrast is Nim's policy on tabs and spaces for indentation.

Spaces are fine. You can use as many as you want. Two, four, three, whatever. Nim doesn't care.

But Tabs? They are forbidden!

Unless you use this magic line at the top of each source file:

  #? replace(sub = "\t", by = " ")
Now you get to use tabs!

I thought this feature was a bit odd once I first started using Nim. But by now I'm a huge fan, and I'll explain why. But first I'll just say that yes, it is slightly harder to search for identifiers. But libraries will stick to either snake_case or camelCase for their identifiers, so as long as you know which one it is it's not that big of a deal. The benefit of this style insensitivity though is really nice. I programmed in Python and C for many years before trying out Nim. And in both languages I've run into libraries that does the opposite of what the style guide recommends. Some people just prefer one style over another and will do their libraries in their preferred style. The problem is that this meant that my code ended up as a hodge-podge of the different styles. This was definitely more prevalent with Python code, but I've run into it with C as well, especially with micro-controller programming. With style-insensitivity however this is a complete none issue. You can write your code in whatever style you prefer, and not care about what dubious stylistic choices the library maintainer sticks to. To me this far outweighs the small inconvenience that it brings (especially if you take into account editor tools).

I feel most people who read about style insensitivity imagines all Nim code being written as a crazy mix of styles, while in reality Nim code is often more consistent in style.

And the tabs vs. spaces issue is simply to alleviate the issues with "how many spaces are a tab" which is impossible to guess when compiling code. The replace filter you mention above essentially just explicitly specifies how many spaces you intend a tab to be. That being said I would've liked it better if it forced tabs, that way you'd have one tab for one indentation level, and everyone could choose their own preference of indentation.

> I would've liked it better if it forced tabs, that way you'd have one tab for one indentation level, and everyone could choose their own preference of indentation.

The problem with this is that in many code bases, you occasionally need spaces to align code/data in different lines to make them more readable and easily inspectable, e.g. within a long parenthesized expression spanning multiple lines. In that case, indentation tabs require nontrivial gymnastics, whereas spaces are consistent.

The files should always contain spaces - because behaving as if spaces are tabs is round-trippable even when every user uses a different setting. An editor could en-tab on load, de-tab on save, and everyone is happy (though I'm not aware of any editor that does this). If the file only has tabs, the converse "de-tab on load and en-tab on save" does not give you the same alignment flexibility.

Nim is actually case sensitive on the first character only, so your list of identifiers are not all equivalent. Just thought I'd point that out for correctness sake.

Oh, thank you for the correction, and sorry for the misinformation!

In any case (pun intended?) it's the underscore insensitivity that makes it difficult to use standard search tools.

Again just for correctness your last identifier isn't legal in Nim. You aren't allowed to start an identifier with an underscore, and you can't have two underscores together.

> it's the underscore insensitivity that makes it difficult to use standard search tools.

I personally don't like the feature, but what do you find difficult about using /my_?[vV]ariable/ to search for any of myvariable, my_variable or myVariable?

Sounds like a nightmare.

Sounds like a pointless misfeature that will only generate mandatory "do not @#$$ do this" entries in future Nim coding style documents, and Nim linting programs that find and flag abuses.

The motivation is good, if the intent is to get rid of ___unwanted___crap___ like this.

But if I, as a language designer, wanted to ban such identifiers, I would just go ahead and ban them, rather than making them equivalent to ones that do not have repeated underscores.

The principle is: don't make unwanted/undesirable forms equivalent to acceptable forms in hopes that people will then just stick to the acceptable forms when they discover that the the unwanted forms don't bring about the difference they were hoping for. People won't. People will go to town with the equivalence to make problems for other people.

People who don't know about the equivalences, or have forgotten, will be tripped up. Someone might think that a variable called __foo in an inner scope is different from an outer (or global) _foo, yet their definition will shadow _foo, with some behavior-altering consequences.

It's possible to argue about this endlessly from a theoretical perspective, but Nim is not a new language, there are substantial code bases (not as substantial as C, Java or Python, of course, but still substantial), and these rules have been worked out through real world cases, and are very effective in practice.

From a theoretical perspective, your argument can equally be applied to C's case sensitivity or Pascal's case insensitivity when someone "doesn't know" or "forgets" about the equivalece - isn't it absurd that FOO and foo are different (C) / equivalent (Pascal) when you are coming from the other one? Similarly, Lisp-1 vs. Lisp-2 . In practice, it's just one more convention -- among several others.

The motivation is not to get rid of __unwanted___crap___ (nim bans multiple sequential underscores and leading underscores, so they are rid of), but rather: Nim arguably has the best built-in FFI of any modern language with nontrivial use, and this FFI has been a factor in the language design. Nim's rules allow you to keep a mostly uniform coding style in _your_ parts, yet integrate it naturally into projects using snake_case, CamelCase, javaCase and ALLCAPSCASE and SHOUTING_MATCH_CASE, or all of the above in the same project.

Nim started out case-insensitive (which is less popular, but definitely common choice made e.g. by Pascal, Excel and others). IIRC, the "first letter's case does matter" is a relatively recent addition (as in, 2 years out of the project's 9) to simplify FFI to conventions like OpenGL, which have the same identifier in both lower or upper case.

In theory, everything could go wrong. In practice, Nim gets it exceptionally right.

Are you saying that the FFI transparently renames identifiers, so you think you're calling foo_bar, but the actual foreign function is FooBar, with no traces of FooBar in the program (like in some definition which indicates that the two are mapped together)?

If so, that's an incredibly bad idea.

If a program calls some foreign function called FooBar, the identifier FooBar better appear somewhere in it, if you know what's good for the maintainer seven years from now.

The “real” name and argument types of an FFI must appear at least once, but other than that the identifier follows Nim equivalence rules.

Again, the theory can be argued endlessly. In practice, it just works.

Ah, but does the real name appear together with the Nim name in some line of code that binds them together, which your editor can jump to if you want to know where that Nim name is defined?

If it's just some construct that defines a call to FooBar in the foreign library, with no mention of foo_bar, but elsewhere in the Nim code we call it as foo_bar, I'm afraid I cannot agree with this being a good technical decision in language design.

I wouldn't sign off on such a concealment ruse even if it were someone's macro, not being upstreamed into a language implementation at all.

That _is_ the nim name. It's just case-and-underscore insensitive so you definitely can if your editor is Nim aware (at the very least emacs, vim and vscode have been aware for a long time; nimgrep is a command line tool similar to grep that is aware of nim's sensitivity rules.

You don't have to agree, you don't have to like it, you don't have to use Nim, and you don't have to think it's a good idea. But any reasonable sampling of real world use shows that it's not a bad technical decision. I don't think it shows it's a great technical decision either; It just shows that it's acceptable and comparable in mental and operational burden to any other decision.

It seems whether it is a little better or a little worse, it will take years of experiment to get any kind of statistical significance.

> That _is_ the nim name.

Well, a nim name; there is no the nim name.

> you don't have to use Nim,

That would be the lucky situation for now; if we band together and write cogent criticisms, we may be able to keep it that way going forward.

This is incorrect.

Nim does not allow variables starting with underscore.

Also, the compiler errors cannot if any of "useHTTP", "usehttp" or "use_http" is acceptable or unwanted, but it can error out with a clear warning if you are trying to define different variables with those names in the same scope.

It's a misfeature, but let's not to be melodramatic.. just use a formatter.

It's a pretty bad misfeature though, since it's a feature you might accidentally use without realizing it if you don't use a linter.

I disagree with this claim.

You shouldn't modify code in a programming language you don't have sufficient familiarity with - at least not code that you depend on.

C has shortcut boolean ops, so "if (have_peace_treaty || launch_missiles() == LM_SUCCESS) cross_border()" would not launch missiles if you have a peace treaty.

C++ has shortcut boolean ops as well... unless you have overloaded "operator&&". So the same line in C++, depending on the types involved, might not short-circuit and start a war even if you have a peace treaty.

In Pascal and Python 3, evaluating 1/(1/2) gives 2. In python 2 and C, you get an integer overflow (with differing semantics). But 1.0/(1.0/2) gives 2 in all cases, even though 1==1.0 is true in all languages mentioned.

And "if (0.3*3 == 0.9) all_is_well(); else apocalypse_go_to_bunker()" might also surprise you if you are not aware of how FP math is implemented (in APL/K/J, all_is_well(), but not in any other language I'm familiar with).

The only example I can think of that could bite you is when shadowing an outer scope -- which is just as much a problem in C, Python etc. Some compilers warn about it, some don't, I don't know if Nim does.

In practice, Nim choices put it in an amazingly sweet spot.

Tabs are fine and spaces are fine, but mixing tabs and spaces for indentation is a recipe for disaster, because different environments and tools have a different tab width setting -- as a result, "printing a file and OCRing it" (which people do, e.g. when typing code shown somewhere else) can result in legal but semantically very different programs looking the same. That's why you shouldn't use tabs on code that is ever edited/viewed by more than one person[0], and why you should always use python with -tt mode.

Nim takes a practical approach by banning tabs altogether; The "#?" hack you suggest just shows another great Nim feature - source code filters are standardized; You don't need a preprocessor/lex/yacc/re2c/swig with its own driver/makefile; it's all well documented and tracked within your Nim environment.

[0] especially in languages in which indentation changes program semantics, but even in e.g. C - where mismatch between indentation and curly brackets can let a bug like "goto fail;" hide in plain sight.

No tabs? Guess I won't be using nim.

Banned tabs? Good to know. From now on I'll just ignore everything about Nim. I will also applaud the Go team's decision to include a formatter instead of such nightmare restrictions.

Well, while its your right to avoid Nim for its syntax choices (I dislike the case insensitively strongly too), the tone of the comment makes one wanna reply "Just don't let the door hit you on your way out".

It's not as if some non-user of Nim announcing they'll avoid the language is any great loss.

Reminds me of all those "Cancel my subscription" letters to the editor in days past. Yeah, I'm the the "Time" magazine or whatever would tremble to know someone is cancelling...

Gofmt is pretty great, I really hope more languages take such an opinionated approach!

nim has a formatter

VB.net is case-insensitive, and probably more popular than you think[1].

I think that case insensitivity actually does make sense when working with symbolic identifiers. I've seen justifications for "Foo" and "foo" being distinct symbols; personally, I find that playing with casing like that reduces my ability to read code quickly.

Case insensitivity does have its downsides, but usually it's not as noticeable in VB.net because most development is done in VS, which automatically re-cases symbols to match the original definition.

[1] Number 6 based on search engine popularity according to https://www.tiobe.com/tiobe-index/ Also, anecdotally, it's used quite a bit in industrial automation.

SQL also does it (albeit inconsistently).

With a tool which removes case inconsistency, it's almost like a language which is case-sensitive.

Almost, but not exactly. For example, you still can't have multiple symbols in the same scope that vary only by case. Which, personally, I'm okay with.

It's shocking how people whine about case insensitivity without bothering to try using Nim for a while.

Case insensitivity is a feature and it's meant to allow easy interfacing with C.

And it also encourages clean code.

With other languages you can have variables called "startdate", "start_date" and "startDate" in the same scope leading to bugs - especially when using completion in an editor - and poor readability.

In Nim the compiler tells you that you are redefining the same variable in 3 different places and you can go and give them more meaningful names.

Some of us work in regulated industries where coding standards are not strong suggestions but unconditional requirements. Unambiguous names and identifiers precludes the use of Nim as a tool in any of those fields just by an improper design decision. Sounds very bad and completely avoidable to me.

There is a reason old languages were many times case insensitive but not anymore.

Do they preclude using e.g. NTFS (case preserving but insensitive) in your project for that reason? NTFS vs. ext4 has exactly the same kind of issues (and it has in fact caused security issues in cross-platform software.

Regardless, this does not preclude the use of Nim any more than a regulation saying "do not use preprocessor macros" preclude C because you might accidentally use #define. It just means you have to verify that you adhere to those regulations. Here's one tool[0] you can use to make sure your unconditional requirements are satisfied.

Nim would probably be precluded anyway based on age and popularity, and rightly so for tightly regulated industries -- but given some time, assuming it does grow in popularity, there is no inherent reason Nim would be precluded from any project that allows e.g. C++, Java, JavaScript or Go.

[0] https://github.com/FedericoCeratto/nimfmt

I'm confused how unconditional requirements would preclude Nim? If anything, this allows you to use your "house style" internally and still interact with libraries who don't. I haven't used Nim, but I imagine that was the intention because that's when I've had to mix styles in other languages. To keep your own code consistent you just need your own linter/formatter, which if you have a strict style you're probably already doing.

Please provide some examples of how Nim is not compatible with some coding standards.

> In Nim the compiler tells you that you are redefining the same variable in 3 different places and you can go and give them more meaningful names.

If this is the goal, there are many alternative ways to solve this problem, making the language case insensitive is a pretty big hammer.

See gtrs' comment above[0]. Why do you assume case sensitivity is "the right thing"?

[0] https://news.ycombinator.com/item?id=21403387

I’m confused so you are saying the language where using StartDate one place and start_date another without issues is better than the ones that error out because you used different names? Seems like a recipe for disaster like the early days of IE that would try to render broken markup, which led to the web being littered with broken webpages that relied on IE specific quirky attempts to save them in order to be consistent.

As an admittedly amateur programmer, I think the reasoning is solid. Its difficult to read and understand code that does things like myvalue = myValue. I think they made the right tradeoff, in that value != Value (the case of the first letter is used), so if you really need to have to values with the same name, you can still do it.

As an aside, is there some reason that using the same identifier with different cases is desirable?

> As an aside, is there some reason that using the same identifier with different cases is desirable?

It's not like that. The thing is that having case insensitive identifiers incentivices people to be inconsistent with their casing "because it doesn't matter" as one coworker that works in a case insensitive language said to me. "Whoops, I wrote 'vALUE'. Well, it doesn't matter; it works. :)" Thank god I don't have to touch that codebase. I do wonder on average how many different variations of casing is used for each variable and keyword.

... It's like using curly braces to delimit scope incentivizes people to be inconsistent with indentation because "it doesn't matter".

Yes, there are always people who would do stupid stuff.

But we have decades of experience with Pascal, SQL, VB, VB.Net, Excel and variety of other languages. Decades. They each have their problems, but in wide practice, case [in]sensitivity is not one of them.

Yet inconsistent indentation is a problem in wide practice. It doesn't take long to find popular open source projects that mix tabs and spaces in their indentation.

Of course, many people are going to be consistent with their casing even when using a case insensitive language, but it's nice to know that particular forms of bad style are never going to be an issue because particular rules of good style are enforced by the language.

Inconsistent indentation is NOT a problem in practice.

inconsistent tab/space use is a problem in practice (not a "wide" one in my experience, but not negligible), but if you use the right tab width (and many projects mark it inside the files with a hint e.g. Emacs understands so that won't be an issue) - then it becomes a non problem too.

I've never heard of someone saying "I don't care about mixing spaces and tabs because it's the curly braces that matter", and I've never heard of someone who edits a file with inconsistent on-screen indentation and doesn't care.

> Inconsistent indentation is NOT a problem in practice. inconsistent tab/space use is a problem in practice

Alright, I was talking about the latter, which causes the former when you're not evaluating file modelines.

> not a "wide" one in my experience

I have a ~/build directory that contains source files of 207 projects whose source I've downloaded over the years for various reasons. Of those 207, if I look for mixes of tabs and spaces in indentations using:

  for p in ~/build/{pkg,repo}/*; do
    grep -IPlr '^\s*( \t|\t )' $p/^.git | head -1
  done | wc -l
I get 144. You may consider the pattern `\t ` ok though because some people think it's cool to use tabs for indentation and spaces for alignment, but if I exclude those:

  for p in ~/build/{pkg,repo}/*; do
    grep -IPlr '^\s* \t' $p/^.git | head -1      
  done | wc -l
I still get 110. Now, those are just the ones that have a space and then a tab in one indentation. How about those that use a space as the first character in one indentation and then a tab as an adjacent line's first character in its indentation?

  for p in ~/build/{pkg,repo}/*; do
    ag -lr '^ .*\n\t|^\t.*\n |^\s* \t' $p/^.git | head -1 
  done | wc -l                       
That's 142.

If I run that last one only against pkg/, which contains mostly official package sources of my distro, then I get 69 matches out of 89 packages.

Note these are not file counts, but project counts with at least one such file.

You may view this as using a small sample size, but at least, the practice is pretty wide in my experience.

I forgot to mention:

> but if you use the right tab width (and many projects mark it inside the files with a hint e.g. Emacs understands so that won't be an issue) - then it becomes a non problem too.

This isn't going to work when you're looking at a diff/patch, emailing an excerpt or otherwise looking at the code outside of a particular editor.

> As an aside, is there some reason that using the same identifier with different cases is desirable?

It is mostly for conventions, however different cases are generally used to represent different things. For example, in Python:

- ALL_CAPS: global variables

- PascalCase: for classes, and now that Python has typing annotations, types

- snake_case: everything else, from variables to function/method names

(This is not a rule, Python allows anything anywhere, so it is just a convention).

So for example, in Nim you couldn't have PERSON (global variable) and Person (class), however you could still have Person (class definition) and person (class instance). Of course, applying the code style from a language to another language is silly, however it is just to illustrate the value of case sensitive identifiers (if you think that my example is more or less valuable than having the flexibility of using a external library the way you want this is your choice).

BTW, nowadays I am studying Golang and I found it annoying that you capitalization is meaningful in Go, so person() is a private function however Person() is public. It is generally not bad, however sometimes I want to create a global private constant and can't simple call it MY_CONSTANT since this would make it public.

You get used to the capitalisation. In python the _ prefix threw me off at first as well, but it just kind of grows on you I suppose.

> Its difficult to read and understand code that does things like myvalue = myValue. I think they made the right tradeoff

They could enforce case consistency instead of case insensitivity if that's the problem they're trying to solve, though.

Although, even then, consider that variables like 'inform' and 'inForm' might not mean the same thing...

Oh, but that's OK. You can just have inform and in_form.

Except you can't because Nim also ignores underscores in identifiers. Gotta make sure there is literally no way to disambiguate cases like this!

(I _want_ to like Nim but this single decision seems to me so spectacularly bad that it's a non-starter.)

I was evangelizing and pushing Python in 1992 (Version 0.9.4) where I worked. The main objection everyone had to even trying ("so spectacularly bad it's a non starter" - think I've heard this wording exactly more than once): Indentation for scoping. I still had to fight that notion in 2010. but thankfully not any more.

Now, that does not mean Nim's underscore insensitivity is a good idea, but excuse me for saying "if that's a non starter for you to even trying, I have no respect for your opinion on programming language design". If you actually try and and dislike it, we might be able to discuss merits or opinions. "Ugly" it may be, it is in the eyes of the beholder. But "bad" - what's your metric?

(Apologies for the greatly-delayed reply: I didn't notice your comment for ages.)

I'm not sure I understand your argument. It seems to go like this: "Years ago some people said one of Python's design choices was terrible, and it turned out to be fine. So if you think a language's design choice is terrible, then I have no respect for your opinion."

I don't see how that makes any sense. (I mean, obviously you are welcome not to respect my opinions! But I don't understand the reason you give.) Some possible language design choices are terrible, no? To take some extreme examples, we can probably agree that the choices made by INTERCAL, Brainfuck and Malbolge are (deliberately) terrible. So, you surely can't actually think that thinking a design decision is game-endingly bad, as such, means having un-respect-worthy opinions.

Maybe what you actually mean is: Deciding not even to try using a language because of one design feature you find terrible is evidence of incompetence, and therefore if I do that then my opinions shouldn't be respected. But, again, surely that's obviously wrong; I don't need to try programming in INTERCAL to know that it's not going to be a good use of my time (unless as a joke).

My best guess (which of course could be wrong, and I welcome corrections) is that you mean something like this: "It should be obvious that this design decision in Nim is no weirder or worse or more unreasonable than Python's decision to make whitespace significant. Lots of people thought that was obviously unacceptable, and with hindsight we can see that they were silly. Since this feature of Nim is no more unreasonable than that feature of Python, you're being silly too."

But I think it's incorrect to say that this design decision in Nim is no worse than significant whitespace in Python. Or to say that I've no more reason to think it bad than people exposed to Python early in its existence. (I also think those people weren't necessarily silly if they thought significant whitespace was a terrible idea, though I think they were wrong to think that. I would not discount someone's opinions on programming language design merely because I found they'd thought Python's significant whitespace was a bad idea when they first encountered it.)

Nim's variable-name rules have the following consequences.

1. Standard tooling will do the wrong thing with Nim names. If you want to search your Nim codebase using grep, or index its identifiers using ctags, or find the next instance of a given identifier in the file you're editing with your favourite text editor, then you're out of luck. (Unless your favourite text editor happens to have added special-case support for Nim. Some editors do have some Nim support; e.g., there are packages for Vim and Emacs that do this. But there are some very basic things they don't do -- e.g., I would like the * key in Vim or C-s in Emacs to be able to search for identifiers in my code, and it doesn't look to me as if this is something the available packages provide.) Consider also searching on the internet; do you want to bet on Google and other search engines learning to interpret things that might be Nim identifiers according to Nim's rules?

2. There are some (admittedly rare) distinctions that there's simply no way to make in Nim identifiers. As an example, someone upthread mentioned "inform" versus "in form". I wouldn't expect this sort of thing to matter often, but as a matter of principle it seems to me extremely poor taste to have a set of identifiers that simply can't represent arbitrary short English phrases unambiguously. (It's a bit like in old versions of C, where compilers and linkers weren't required to be able to distinguish anything more than the first 6 characters of each identifier, case-insensitively. You can always work around it, but having such a restriction there at all is a sign that someone hasn't thought things through.)

These are genuine limitations imposed by Nim's identifier-name rules. There are obvious easy-to-foresee ways in which they would lead to inconvenience and trouble when using Nim. Of course, it might turn out that the advantages (in, e.g., letting you use external libraries with a variety of stylistic conventions, while keeping a single style for your own code) outweigh the disadvantages, in which case I'll have made a mistake in not wanting to pursue Nim further for this reason. I'm comfortable with that; I have a finite amount of time and have to choose somehow what things are worth spending the time to learn well, and inevitably sometimes those decisions won't be optimal.

So far as I can see, there is (and was in 1992) no objection to semantically-significant whitespace in Python that is (or was) as cogent as this. There was "it's weird and unfamiliar" (which, in fact, I think can be a perfectly adequate reason not to put time into something; again, time and effort are limited and one has to prioritize somehow), and "it makes copying and pasting more error-prone sometimes" (which I think was never a strong objection for anyone using anything smarter than Notepad for editing, because you need to be able to fix up indentation whether you have block delimiters or not), and that's about it.

So, to me right now, it doesn't look as if the situations are parallel. Of course I might be wrong! Maybe back in 1992 there were objections to Python's whitespace rules that were just as apparently-cogent as the objections above to Nim's identifier-name rules are now, and I've just forgotten what they were or how plausible they seemed. But I don't think so.

(Pedantic note: Python doesn't use indentation for scoping. It uses indentation to delineate block structure, and some languages that are not Python use block structure for scoping. Python's blocks do not in general create their own scopes.)

You can also just pick a different name, which is something you should probably do anyway in this case.

Sounds like you're agreeing with me?

Yup. (And saying it's _even worse_ than what you describe.)

In many languages, you might use one for a type name, the other for a variable.

You could ban using the same identifier with different cases, instead of treating them the same.

> In many languages, you might use one for a type name, the other for a variable.

And Nim is one of those languages too. This is perfectly valid and frequently used:

      Person = object
        name: string
        age: int

    let person = Person(name: "you", age: 99)

    echo person

You can try it in Nim Playground: https://play.nim-lang.org/#ix=20jS

On the other hand, what if it's a code UI limitation? Suppose the UI for writing Nim had support for linking, editing, viewing all such alternately spelled but equivalent symbols?

There are tools that aren't nim-specific, like ack/grep/sed/git, and code review tools, which suffer as a result.

Then it limits me as a programmer to have to use said UI...

Nothing unusual in doing e.g. `array = Array()` (although you should probably give your variables better names than that), which would break in Nim.

> `array = Array()` ...which would break in Nim.

This is NOT true.

The first character is case-sensitive in Nim, so examples like yours are not only possible, but frequently used even in Nim compiler and standard library.

Right, and that's the point, aside from the fact those 2 are actually different in nim. I see what you are doing there, but that would have been very confusing when I started programming.

Question is, should the language enforce you giving your variables a better name, or should convention? I think there are reasonable arguments for both.

But does a language with Python-like syntax really have to try to be so "beginner-friendly" that it enables inconsistent casing?

The language itself can't enforce better variable names, at least not this way - Nim doesn't stop me from using "abcd" or "a0, a1, ... a100" as variable names even though it's usually a very bad idea.

No, I think a language should help keeping the code consistent in the long term - and having this strange case insensitivity doesn't help there in my opinion.

> I think a language should help keeping the code consistent in the long term - and having this strange case insensitivity doesn't help there in my opinion.

Not at all. Style insensitivity encourages good variable naming and there are both formatters and also the simple text completion in editors to guarantee consistency.

Another perfectly fine example:

  auto * logger = Logger::getLogger("main");

...which also works perfectly fine in Nim.

But you’re not fully solving the problem of devs using inappropriate names and you make using popular tools like text editors, grep, ctags, etc more difficult to use effectively. I mean how do you stop devs picking easily confused pairs of identifiers like “xxxxxxxx” and “xxxxxxxx” for example?

Feels like a poor design decision to me based on cost/benefit of user ergonomics.

> As an aside, is there some reason that using the same identifier with different cases is desirable?

I do it all the time, in numerical code. If you have two pairs of points, for example, it is very natural to name them "a,A,b,B"

> for example, it is very natural to name them "a,A,b,B"

And just to clarify once again, since I'm not sure who will read which reply: those are NOT the same identifiers in Nim!

      a = 11
      b = 22
      A = 99
      B = 88

    echo a == A # false
    echo b == B # false
Run the code in the Nim Playground if you don't believe: https://play.nim-lang.org/#ix=20kr


then I don't really understand nim's case insensitivity rules

Only the first char is case sensitive. The rest is case and underline insensitive;

Oh, my god! How do people reach this kind of ideas?

Nim is written in Nim and no one has run into this style insensitivity confusion despite many people contributing. If your project is smaller or fewer people and you still think this is a big issue, good luck. And if it is larger, this is the least of your concerns.

Think in reverse, is it a useful language feature to have MyNimName, mynimname, MY_NIM_NAME, My_Nim_Name, __MYNIM___name_ be different? When did you need this flexibility recently? Are we really so limited at naming things appropriately?

It's fine to make a big deal about something in theory but if it really was such a dumb idea it would have died over the decade Nim has been around. It was debated again prior to 1.0 and persisted, several good reasons are also mentioned in this thread. And it isn't even an original idea as brought up by several others.

If this is the reason someone doesn't try Nim then it's only unfortunate for them. Add this to the tabs vs. spaces, curlies vs. whitespace indentation or other so called concerns which make no difference in the grand scheme of things.

Main reason for this feature is that Nim is big as a glue language. Many C/C++ libraries don't have consistent naming conventions between them. I don't want my code to look like a patch work of `__APICALL__(system_call.inspectSomething())` I just want it to look like Nim code: `apiCall(systemCall.inspectSomething())`. This is just a consistent standard on how to turn names in one style into another style backed into a language.

    apiCall = __APICALL__

So you are writing hundreds of rename rules.

Well, unlike treeform, I like it to be:

Which I find much more readable. What now?

Then you are writing or auto generating these giant 1000s of lines long files with renames... why?

I never said it was a good idea!

Every good language must have some batshit crazy bizarro wart thrown in...

Not true. Powershell is mainstream and case insensitive. Never ran into an issue with it or heard of people complaining about it.

You can use nimgrep [0] to find symbols that match each other with case insensitivity, but yeah it is unusual.

[0] https://nim-lang.org/docs/nimgrep.html

nimgrep's alright. I was thinking of code search on, say, github. I feel the additional complexity was unnecessary in an otherwise well designed language.

This. I like many of the things that Nim is doing, but I refuse to take it seriously if it's going to make basic things like tag search and grepping difficult.

inb4 a Nim linter is written to force a consistent naming style throughout a codebase.

> inb4 a Nim linter is written to force a consistent naming style throughout a codebase.

Already done:


> Detect inconsistent variable/function naming

HA well there you go. This shouldn't have to exist.

you can compile with `--stylecheck:error/hint` for enforcing consistent naming.


Fortran and Ada, as well.

Disallowing these case/underscore variants from referring to differing values could be quite ok -- even a plus-- if (but only if) an additional rule is added to disallow any variations of the declaration-time casing for a variable within the same scope to prevent the need for bespoke grepping tools and discourage arbitrary variations. If that restriction was added one could argue that this improves code readability (& "listenability") by have fewer variable names available in the same scope which would sound the same but aren't.

Even with such a rule, you would still run into problems if you want to grep for a token that's used in some application but declared in a separate library.

IIRC a study on beginners learning Python found that case sensitivity was the biggest source of errors/confusion. So I'm all for new languages being case-insensitive.

Another precedent for case weirdness :-)


I've been using Linux since the late 90s and case sensitivity has never been an issue for me with Nim since I started using it in 2016. This is FUD and fake news.

Shameless plug - I interviewed Andreas, the creator of Nim, about his philosophy and design choices here: https://sourcesort.com/interview/andreas-rumpf-on-creating-a...

Might be an interesting read for people who are thinking about using Nim and wondering how it compares to the C family, Python, Go, etc.

> The C-family of languages has quirky syntax, grossly unsafe semantics and slow compilers but is overall quite flexible to use. This is mostly thanks to its meta-programming features like the preprocessor and, in C++'s case, to templates.

> The Pascal family of languages has an unpleasant, overly verbose syntax but fast compilers. It also has stronger type systems and extensive runtime checks make it far safer to use. However, it lacks most of the metaprogramming capabilities that I wanted to see in a language.

While Nim somehow is not appealing to me, this absolutely nails it.

If you like reading stuff like this, you might love the book, The Design and Evolution of C++, by Stroustroup.

You know the language has potential when the top comments on HN are all about syntax.

To generate more talking points, somebody ought to write an inverse of dumpLisp: https://nim-lang.org/docs/macros.html#dumpLisp.m%2Cuntyped

I am confused if you this is an honest opinion or if you are saying with a touch of sarcasm. If it is the latter, a winking smiley or "</sarcasm>" or "/s" would have been nice to avoid triggering Poe's Law.

In my honest opinion, discussion on syntax is a distraction. One of the reasons I like Lisp/Clojure is that there is very little syntax. I can just focus on my problem and writing good code.

I think he meant that if the worst thing people can say about a language is that it has slightly quirky syntax it must've done a lot of things right.

I don't think it speaks to potential - it's just Wadler's Law ;)

I've been considering getting into Nim for a while now as something more lightweight/terse to write thin CLI utilities in. I'm glad to hear that it's performing well for this usecase. It's a very attractive alternative to Rust for it's simplicity and terseness.

I still haven't gotten around to picking it up though, these days I just use QuickJS for anything that needs to be reasonably portable and thin.

Maybe next side project...

I would highly recommend it. Setup is incredibly easy (VSCode has great plugins for it), the language is simple to read/write and compiled Nim is very fast with small binaries. I also really like nimfmt to keep my code tidy so I don't need to worry about it. Nim compiles VERY quickly and errors seem to be easy to comprehend. It's definitely one of my go-to languages for most of my projects.

Nim is great for knocking out commandline apps, I recommend "cligen" for quick and clean options, or "docopt" for friendlier cli options. They feel "scripty" when you can begin with a couple lines and grow it up from there. I found it the easiest to learn. And difficulties getting my other favorite, D, on embedded and Haiku and OS/2 (which also have poor javascript support) was my prompt to give it a try. I found cross-compiling easy. But just as easy was having Nim installed on all my platforms.

Is there anyone using Nim either in side projects or in production that can comment on how they like it?

I keep hearing about Nim and it sounds interesting, but I'm not sure I have the mental capacity right now to do a deep dive into the language and build something with it. I'd like to at some point soon though.

I use Nim in production. I been using it for more than 1 year in production.

I like it. For me, it started out as a faster python that prevents typos. But it has really grown on me. I really like that I can share libs on server (compiling to c) and client side (compiling to plain javascript).

Basically any C library is also a Nim library with a tiny wrapper. That's a huge ecosystem! I also like how Nim can integrate with complex C++ libs that do virtual table call backs. It can also call objective-C functions .. you know the ones with [foo a:1 b:1] crazy syntax? Python was a glue language for me, but Nim can just glue more things.

JS too! In Nim you can just compile to plain JS, that other JS can call as well. Pass plain JS objects around. I basically replaced my JS code file by file - there was no need for a huge rewrite.

I know docker and kubernetes is "the thing" right now, but I just like that I can just scp the binary over. It just feels less complex. I feel like docker solved the distribution problem for python and node... I just don't have a problem to solve any more. It's just gone.

ditto! docker and kubernetes are solutions to problems that shouldn't be there in the first place!

What problems does it solve? It seems like a convoluted way for distributing shared libraries and support applications with the primary application. Couldn't i just put them all ina folder wih a script to se paths?

I've quit using Python, unless forced, because I've gotten too many folders of files that "work on my(the original developer's) machine," but fail on mine because I didn't find or follow a README that said "just" install all the same dependencies and versions.

I have some code duplication between android, iOS and a webapp / PWA. How realistic is it to look at something like nim for codesharing?

Not the parent poster but I'd say it's very realistic. I've written a few applications that target both JS (web browser) and binary (server) and it works very well. One example of this is https://picheta.me/snake/.

I'm also working on a game that I haven't quite moved to iOS/Android yet, but that is my plan. So far targeting the desktop with SDL and HTML5/Canvas with the JS backend works very well.

Sharing code between Desktop and Mobile still a bit rough. I have done some experiments and it looks promising: https://forum.nim-lang.org/t/5197

Does not appear to be any harder then sharing C or C++ libraries.

I'm curious as to which projects you use it in.

Do you use it in Pushbullet?

I've been using Nim in side projects for the past year or so. I really love the language, to the point where its replaced Python/Node as my language for utilities. Some of the qualities I like:

1) Its really easy to get started with. The default runtime has a GC for memory management, and the typing system is pretty similar to other popular languages. You can pick up the syntax in a day, the basic semantics in a weekend, and the more advanced semantics in a month.

2) Its fast and reliable. If the code compiles, it probably works, and it's probably a tier faster than a scripting language. Maybe not as bulletproof as Rust, but the ease of use is worth it for me. It can even reach C speeds, but that does require some advanced knowledge. Again, the default runtime is probably fast enough.

3) Powerful language features. I know there are a lot of people out there happy using C. I'm glad that works for them, but features like case objects, generics, and closures are part of the abstraction tools that I use to get stuff done. Nim also boosts what might be the best macro system for a static typed language, which is very helpful for framework developers.

I'm using Nim in production since .18 with zero issues. I spent many years in Qt/C++ and was reasonably quick, but not as quick as I was with D. But then, in an evening, from a cold start, using nothing more than the two Nim Tutorial pages that I happened to have cached in my browser (I was away from a network connection) I was able to redesign and reimplement the bulk of my application in Nim that had taken me one week in D and two in C++. Qt/C++ was great to go from desktop to server to android/iphone. Qt is a bit of a burden on embedded, and D didn't cross-compile easily as Nim. I like banging out standalone executables starting from a two line "script" and evolving up to a complex server. I like Python, but find datatype or whitespace issues that crop up only in runtime intolerable. Compiled Nim won't allow that in the first place - and is very helpful with error messages. I'm also infuriated by getting a "cross-platform" Python script that won't run on my machine because I haven't configured and installed all the dependencies that were on the original developer's machine! Anyway, I'd say pick a small app and have a go. After that, you won't want to waste the mental capacity to use anything else. For knocking out commandline apps, I recommend "cligen" for quick and clean options, or "docopt" for friendlier cli options.

I'm curious, what do you use as an alternative to Qt for Nim?

Well, if you're talking GUI stuff, nothing in Nim compares to Qt. But, I did routinely use Qt for console, server, and headless embedded stuff. The Qt environment compensated for lots of C++ shortcomings up to recently. I think I've seen a Nim linkage to QML (Qt's newer Javascript UI markup language). But, I've never been all that keen on it. Depends what you need in a GUI. QML worked well for me on android/ios apps. I guess I'm just old school when it comes to desktop apps, and I can bang out a Qt gui app in a hurry for the desktop. A few times, in D, I'd written all of the logic for the application in D as a console app with a command handler that worked on stdin that would generate the same Qt data model on stdout that a Qt GUI app would use to populate its model-bound tables and whatnot. Worked surprisingly well. But, to end this treatise on the state of nim gui stuff... I've had some fun with nimx, but keep trying the more native stuff. give 'em a try.

I've used Nim a bit for side projects (on and off, even back when it was called Nimrod). I found it really really powerful once I got past the initial bump.

It's a fairly small learning curve to be honest, especially if you're familiar with reading Python.

What I love about it is the small binaries.

Can the binaries be statically compiled similar to go?


I've used Nim for most of my personal projects for the past ~2 years, and it's generally enjoyable. There are a lot of useful language features. C interop, generics, tagged unions (I always miss these in languages without them), traits (powerful duck typing), channels, generators. Metaprogramming via templates/macros if you want to get crazy. Lots of rope to hang yourself, so to speak.

I wouldn't choose to use it in production, mostly because of the developer experience. The documentation and tooling are both pretty bad, and the compiler errors can be arcane (especially when using generics, or when the error only surfaces during C compilation).

Have you checked the latest documentation recently? There has been some really good work in the past few months to improve all these areas.

What doc issues are the biggest pain points for you right now?

(Getting good error messages for macros and meta-programming is notoriously hard, but we are trying!)

I'm using Nim in production (combined with NodeJS) and I really like it. Cross-compiling is so easy. Testing is easy. Syntax is easy to read. I like it.

Edit: Email me if you'd like more info.

I ported a smallish script I use at work (~300 lines or so) from Python to nim fairly easily since the syntax is similar. Being able to compile a small binary across several platforms is nice, though the particular library I used wouldn't compile on Windows using nim 1.0, despite supposedly working in earlier versions (the bugs appear to be in Nim's Windows support, not the library).

The biggest downsides right now as compared to Python are the overall lack of libraries, and lack of documentation (the core language is reasonably well documented, 3rd party libraries much less so).

I used Nim for my implementation of Make A Lisp[1]. I use Python every day and the jump was pretty straightforward. Almost copy and paste from some things. I don't have to think about static types much, but it was a welcome change. I loved having macros to play with, too. I spent a lot of time with docs which aren't bad.

As for the "mental capacity" I felt the same about finally getting into Rust. Spending some time with Nim actually helped me bridge the gap from Python to Rust.

[1] https://github.com/kanaka/mal

I used it in production for a small yet fairly critical utility and it worked out very well.

i used it a about three years back for a few sideprojects. i ran into two or three bugs, but i saw those get fixed in the months after my work. im hopefull that biw at 1.0 things are pretty stable. it was mostly a joy to use. meta programming did generate hard to diagnose errors at the time.

I used Nim for a few projects. My best description is that it's fun, but everything feels kinda off when writing stuff.

Can you please elaborate? What about it feels off?

Author of the article here, feel free to ask me anything if you're wanting to understand something about the article, or about Nim. I am not the author of Nim.

I also want to point out that Nim does have an IRC channel (#nim) on freenode, which was helpful for understanding some of these things.

Nim's syntax is everything I hate in a language.

It's not bad, objectively speaking, but its design opinions are the polar opposite of mine.

I'm half and half. I adore syntactic whitespace and it makes me sad that it hasn't become more prevalent.

However the flexible identifier rules bother me no end. But as soon as my editor of choice handles it transparently then I guess I might come round.

> flexible identifier rule

When / how has this actually impacted you?

It hasn't because I've not done more than merely look at Nim.

However it bothers me aesthetically and has put me off trying it.

How do data scientists feel about Nim? It feels very pythony, but way faster - could be a really cool language for them, similar to what Julia is trying to be.

Curious if anyone in a DS role has tried it out. I'm certainly tempted to try it out, I'm sure I'm losing lots of performance in parts of my analytics pipeline due to Python being garbage slow.

I am trying to improve Nim data science ecosystem with https://github.com/mratsim/Arraymancer.

I think today the best way to try Nim is for slow Python utilities vi nimpy: https://github.com/yglukhov/nimpy

You can also use Nim in Jupyter the following way: https://github.com/apahl/nim_magic

My next step would be to improve interop between Arraymancer tensors and Numpy ndarrays.

Very cool work! Thanks for the links.

Re: speed, I'm seeing the Rust ecosystem get better and better; I'm also learning the language on the weekends with small projects.

But also: many many moons ago I had Matlab code that was really slow (and I wanted to run 10^LARGENUM simulations for Monte Carlo purposes) and decided to learn Fortran. It was surprisingly easy to learn, and it did cut my running time from afternoons to minutes. But in the process I found out that my Matlab performance problems were mostly due to arrays being dynamically resized all the time -- while static memory preallocation could be hacked easily in straight Matlab.

As for Python, numpy is a fast embedded minilanguage written in C; the difference between "pythonic" and "numpythonic" is not emphasized enough in bootcamps, I think.

Matlab usually flags cases like that in the editor suggesting you reserve space outside a loop.

My experience with numpy was that it was 11x slower than visual basic access in excel. I was deali g with a lot of small matricies instead of a big one and I think that contributed.

I want to try julia in production. About to try out rust in production.

Not really a data scientist myself, but using Nim for my PhD in physics to analyze the data I took for an axion search experiment, code at [1].

I just love it. Super productive to work with and in my case faster than the C++ framework I'm replacing.

The ecosystem of course is pretty small. But for me wrapping C code, porting something or reimplementing some libraries can be a lot of fun, too. In my experience every hole I've found was easily plugged so far.

[1] https://github.com/Vindaar/TimepixAnalysis (recently due to ..reasons.. all development happened on the open PR)

I work as a Data Scientist/Machine Learning Engineer and I think that biggest issue with Nim are lacking libraries. Working as a Data Scientist is very iterative process, you spend most of the time preprocessing data. It's often try and see approach so interactive repl is a must. While I know Nim has some library to be used with Jupyter, I found it too immature. Other reason to favor Python is it's libraries - they are often written in cython which yields comparable (but of course a bit slower) performance to C. And to speed up custom code you can almost always use numba (llvm-based jit compiler that works with CPython).

Nim is used by leading data scientists. Eg.: https://github.com/mratsim/Arraymancer

The lack of mature libraries in the style of pandas, sklearn or the tidyverse, and the lack of a stable REPL is a major drawback for "production" data science. On the other hand though I've had fun playing with it in a personal capacity precisely because of the rough edges.

If I want to fit a particular kind of model I'll probably have to roll my own, meaning I have to be familiar with what the algorithm is doing and why, rather than just importing something straight from sklearn.

I also need to pay more attention to the code as I'm writing it instead of lazily debugging by trial and error in the REPL like I can with Python.

Basically, I wouldn't recommend it at work, but it's been fun and useful at home.

Since you mention tidyverse, I might shamelessly mention that I'm currently working on a sort of port of ggplot2 and some dplyr features in Nim [1]. It's super WIP, but for most plots and simple data frame operations (complicated stuff I do beforehand) I need it has mostly replaced plotly [2] (or in some cases matplotlib).

However, don't expect the included data frame to be fast. That in combination with a dynamic nature has been out of scope for me alone.

[1] https://github.com/Vindaar/ggplotnim/tree/addDocs

[2] https://github.com/brentp/nim-plotly

The options in python to speed up your code are vast without really having to do much, on a fundamental level. My outdated experience with Nimrod told me that it was a tad too opinionated E.g. Everything is a string in dbs is mentioned in the article.

It was an interesting language, for me it felt like a stepping stone to something better. But the docs were really bad,

"Nimrod" experience is quite outdated. There have been great improvements in documentation in the last year.

> The options in python to speed up your code are vast without really having to do much

This does not match my experience. The #1 way I see to optimize Python is to not write Python at all, and instead to FFI out.

there is this great discussion about the Nim Language on Julia Discourse: https://discourse.julialang.org/t/version-1-0-released-of-ni...

Is Nim stable? I have vague memories that some parts of its runtime had some deep issues that weren't yet resolved, like maybe around concurrency? I see the language is at 1.0 now, does it have a stable, solid, dependable implementation and runtime?

I don't have vague memories. I use it everyday on production code on server and embedded. I find it stable, solid, dependable.

Can I ask how you got started on embedded and on which MCU? I assume you mean bare metal.

Looks cool already but is it easy to cross-compile? I can copy a Python script from my Linux machine and run it on both Mac and Windows PCs. Can Nim build executables for other platforms nearly as easy?

In addition to compiler switches that allow you to cross compile from the nim compiler [0], you can compile to c or c++, and then invoke whatever cross-compilation toolchain to compile to more esoteric platforms.

[0] https://nim-lang.org/docs/nimc.html#cross-compilation

If cross-compilation is important to you, and you like Nim's level of abstraction, you might have a look at Haxe, which can compile to [lots of different targets](https://haxe.org/documentation/introduction/compiler-targets...).

Yes! I use Nim on macOS to build a Windows exe for bootstrapping a Windows VM. A year ago, you had to dig a little to figure out the right command line incantation, but maybe it's better now.

Well, you sorta can. As long as you wrote it:

* to work in both Python 2 and 3 (no telling what other system's Python will be) * don't use any modules outside of stdlib (that requires install too)

Those aren't always requirements, but they are the reason I reach for compiled languages when I can't control the target environment where the program needs to run (or don't want to).

Yes, Nim is cross-platform.

It also targets C, C++, Objective-C and Javascript, making it possible to run on platforms where using C is not the preferred choice.

cross-compiling to ARM embedded was surprisingly simple

Does nim have any way to write a one-liner that includes nested scopes? Something like `nim --run="if(...) { while(...) { foo; bar;} }"`? Not knowing nim, I just made that syntax up... :-)

What're compile times like?

For me, I'm seeing compile times averaging around 5-10 seconds, with outlier builds taking up to 40 seconds if the files in question aren't cached.

It feels faster than Go, but I think part of that is down to Nim's compile output being verbose by default.

Nim's compilation is much faster than Go.

As someone that compared both a while back (and a biased Nim core developer) I don't think this claim is true. Go is really fast at compilation, but honestly Nim is good enough.

Do you mean compile times for Nim or a project? It's accurate to test it yourself since it's free.

Projects of any note are notoriously hard to test for yourself since they often use build systems. Golang is nice in this respect, pretty easy and fast.

Nim projects typically use `nimble` that ships with the install. So all you would need to do is `nimble build` in the root folder and voilà! Of course that also pulls the dependencies from the package manager, so you might want to just extract the build step from the `.nimble` file if you wanted to time just the compilation.

It's fast, even when you use generics or metaprogramming.

I have a couple of 10k+ LoC codebases.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact