Advent of Nim
nim-lang.org
nim-lang.org
Obviously it also goes into Nim specific features like metaprogramming, interfacing with C and JS and the standard lib features in general.
[1]: https://github.com/nim-lang/nimforum
[2]: https://github.com/dom96/httpbeast
In terms of features it's powerful enough to build a Discourse-like forum enough. In terms of performance it's very well performing in the TechEmpower benchmarks[3] it is competing in. It even grabbed first place on JSON serialization on Cloud hardware.
[3]: https://www.techempower.com/benchmarks/#section=data-r17&hw=... and https://forum.nim-lang.org/t/1152#27100
Disclaimer: he is also my colleague.
[1]: https://github.com/status-im?utf8=&q=&type=&language=nim
The core language is interesting, but like the standard library has an odd edge here and there.
Because it's effectively a transpiler and it's way less opinionated than (say) Rust, I managed to use nim in embedded controllers without any special support from the compiler.
Nim suffers because it doesn't have the big push of a corporation such as Mozilla behind.
- Nim JS for browser display
- Nim C as a backend
In other words, Nim is not a transpiler, it's a compiler that outputs C or Javascript. But the difference is just in terms of the readability and modifiability of the output. For the purpose of using it in embedded controllers, the difference doesn't matter, it's the fact that it generates C code that is of value. And yes, Nim is vastly less "opinionated" than Rust, thank goodness.
I personally believe that they aren't. But I know of a lot of people who disagree.
And there are people who will disagree about just about anything, because they are ill informed or have poor judgment or are dishonest (looking at you, White House) ... the mere fact that there are people who disagree about something is not of significance.
Of course not :)
Yeah, to me Nim is a compiler. However, I can see why people might refer to it as "transpilation" if we are talking Nim->JS. But yeah, please don't call Nim a transpiler, it makes us Nimians all sad.
I've started using Nim couple of months before last year's Advent of Code, after I've been reading about Nim both here and on Reddit.
As a Python user, I was immediately drawn to Nim's elegant syntax, which looked to me like "ok, just copy-paste my Python code, and declare some variables, and voila: 10-100x faster Python!", which is 80% true. The remaining 20% were "this works in Python, why doesn't work in Nim?" questions, which were promptly answered by the great Nim IRC community. I soon realized that those differences are what makes Nim great, and I began to really like the language.
I decided to solve AoC 2017 in both Python and Nim [0] so I could directly compare my solutions. This was a great experience for me, and I've learnt a lot.
In the past year I started using/liking Nim more and more (I have used Nim even at my work (numerical calculations, before done in Numpy)). This year I'm gonna solve AoC solely in Nim.
From my experience, your hunch is right.
If you have any Nim questions, let me know.
Basically, Nim ignores case (except for the first character) and underscores in identifier names, so the following is a valid Nim program and all those names refer to the same variable:
var myVar = 1
my_var = 2
mYVAR = 3
echo(myVAR)
Most people wouldn't actually write code like that, but I personally found it to be a turn-off.More on this: https://nim-lang.org/docs/manual.html#lexical-analysis-ident...
I've seen people write wrappers just to fix casing.
EDIT: you can still change them in importc pragmas, but this still seems less nice to me.
Still, you can reuse Nim libs even if they don't follow nep1: one can still use nimpretty/gofmt for his own codebase.
If you had your own preference one way or the other, you could call a library's `snake_case(foo)` function as `snakeCase(foo)` instead, and pretend that the library matched your own personal style.
Of course, this isn't very useful in practice, and makes simple things like grepping for identifiers or Googling a function that much harder. Tools for refactoring also suffer.
What's better is having a single style guide enforced by ordained tooling; e.g. PEP 8 for Python, Prettier for JavaScript, or gofmt for Go.
Nim on the other hand takes a different approach, don't trust anyone else to keep things the way you like it, but rather allow you to use it however you want. This means that if some inexperienced Nim programmer who didn't care to look at the NEP 1 style guide (yes Nim also has a style guide, which says Nim prefers camelCase), but nonetheless wrote an interesting library could still see his library used by others.
If everyone is using this tool anyway then style insensitivity will not be a problem, and for those cases where some library uses snake_case instead the code formatter will still be able to format your code completely consistently even if you're using that library. There is no way that gofmt or any other language's code formatter can do that.
https://docs.python.org/2/library/queue.html
Before 2.6, the situation was even worse, with camel-case methods in the threading module:
This is such an incredibly small inconvenience, I have no idea why the Nim language would choose to make identifiers case/_ insensitive. Basic refactoring tools become unnecessarily more complicated.
Just searching for an identifier is a complex regex instead of what should just be ctrl+f.
"Basic refactoring tools" would just need to replace the case sensitive search with insensitive. Every editor knows how to do that.
If you're talking about the underscore and first letter rules - yes, they're different, and that's why nim includes nimgrep, and support was added for just about every editor under the sun already (Emacs, VSCode, Vim, Sublime, TextMate at the very least). It's still Ctrl+F
That's something that would have appealed to me years ago, but now not so much.
In 2018, I want all the libraries, and all the code I ever look at in a language to follow the same style and naming conventions. If you adapt yourself to that, my belief is that will aid comprehension in the long term.
So I appreciate the language communities which have strong conventions which are followed by the majority, and are encouraged / enforced by the tooling. All hail gofmt and rustfmt!
I feel like this is mentioned (by the people who just glance over Nim) every time there is some discussion about Nim, and IMO it is blown way out of proportion.
In my cca year and half of using Nim, I not even once had a problem with the style insensitivity.
IMO, you (general you) shouldn't use `my_foo` and `myFoo` for different things, regardless if a language allows for it or not.
Not hard to make up any number of examples where spacing or capitalization matter for good reason. They matter to humans; its just silly to make them not matter to the compiler.
Now back to your concern of identifiers being read in different ways meaning different things. Once you know that this will be an issue you tend to think about it when naming your variables, and so it tends to not really be an issue. The thing is that once you get used to it you know that `pidTarget` and `pIdTarget` is the same thing, so you either relax you expectations, or you don't construct your names with this ambiguity. And besides, a "pointer to a target id" and a "scalar process id" probably don't share the same type, so the compiler should be catching that anyways if you ever used it wrong.
grep -i solves the case issue, and grep/ripgrep probably _should_ have a feature to ignore _ ; in which case you will be able to use them - but until then, nimgrep will do that work.
I don’t know how atheistic style looks, but Nim is style agnostic - everyone can practice their religion and they all get along. C, Python, and the real world - not so much.
This one point is always worth considering when deciding on user-facing portions of technology.
(let ((|hello world| 3)
(|HELLO world| 4)
(|こんにちは (jp)| 5)
(|pi\|pe| 'piped)
(|newlines
work
too| 6))
(list |hello world| |HELLO world|
|こんにちは (jp)| |pi\|pe|
|newlines
work
too|))
; --> (3 4 5 PIPED 6)Search HN (box at bottom of page) for Perl6 and you will find many articles with references to kebab-case.
As mhd said, it's used in Lisp too.
Not to mention, in my opinion it's more aesthetically pleasing, but that's just me :)
I did not know this about Nim and does also seem quite a big turn off and a bit of a WTF decision
I have never coded any Nim, just read about it though :)
I guess the intention is NOT to let you carelessly use Myvar, MyVar and myvar willy-nilly in your code, as I had first imagined
I assume the intention is to PREVENT you from deliberately defining those names as different vars, which would be confusing
So, on reflection, maybe it is not such a WTF
But is not a deal breaker for me. If the language doesn't let me I can use `ii` or `i2` or whatever.
Please notice that the first letter in Nim is case-sensitive, so `I` != `i`, and you can continue use your conventions.
And before some new comment starts bashing that, I think this is a must so you can differentiate your types, written in PascalCase, from your variables, written in camelCase.
Using name completion in my editor is enough to keep the naming consistent. Try the language for a month before deciding.
Also, the survey indicates that very few people are turned off by this feature:
https://nim-lang.org/blog/2018/10/27/community-survey-result...
int main() {
int myVar = 1;
int my_var = 2;
int mYVAR = 3;
int myVAR = 4;
// ...
}
I have seen a few codes in my life where complex routines with multiple nest levels had variables with names "x", "X", "_x", etc.It saved me few bugs already.
It's way too easy to ignore (or even embrace) the identifier equality rules in Nim to make this the hill you die on. My story's very similar to other responses - I've been using Nim for quite a while and these identifier rules have never been an issue.
I strongly suggest anyone hung up on this monster under the bed try Nim anyway as it's really pleasant to use. It's sad that all Nim's benefits are sometimes drowned by mostly unfounded hangups on this identifier thing.
Anyway, don't worry: I didn't die on that hill. I've had the opportunity to spend some more time on Nim since.
There was a set amount of time available to spend on Nim at that point, and all that time went into finding out why nimgrep exists in the first place, then reading up on the background.
It didn't help that all nimgrep's documentation had to say on the matter was "Nimgrep has particularly good support for Nim's eccentric style insensitivity", which doesn't really tell you anything unless you already know about Nim's style insensitivity.
I would love to find a language for game / engine development that's as performant as C++ and as easy to use as C# or Python, but this issue scares me. If I search for a variable using any of my existing plain text search tools, I expect to find the variable. It makes me think that this isn't a language that's meant to be used by large teams on large, long-lived codebases.
I think you are over-estimating how inconsistent Nim code typically is. I have been coding in Nim for a very long time and I personally just use case insensitive search (which I use for all languages anyway).
It's easy to tell from context what the identifier will be. If the code base is snake_case then I search for `foo_bar` if not then I search for `fooBar`. I cannot remember a single time this has failed me, and even if it does then the worst case is you'll need to search for the other alternative. There is really no trouble here and I feel like a lot of people, when they see this feature, worry too much because it's so different. Please please please reconsider, I know it's a strange feature and it doesn't feel worth it, but it really isn't a problem at all.
But mostly, it just seems like a really weird call to make for the language. There are real disadvantages to it -- I can't really rely on my search tools. So that... when you're using a library you can refer to its identifiers using a different capitalization convention if you want to--why is that important?
I don't know, it just seems like a really weird call to make, and if that's one of the first things I've learned about the language it makes me think there are probably plenty of other weird things like it.
But why? I personally haven't experienced this having worked in a big Nim project. I'm really curious why you think a bigger project could become burdened by this.
> There are real disadvantages to it -- I can't really rely on my search tools.
I disagree. You can rely on your search tools, as I've demonstrated in my previous reply. I feel like you're thinking of the worst case scenario and convincing yourself that it's very common. It simply isn't, people don't create crazy identifiers, why would they?
> So that... when you're using a library you can refer to its identifiers using a different capitalization convention if you want to--why is that important?
It's important for consistencies sake. In the real world developers prefer different conventions, so you undoubtedly run into a library which uses a convention that is different to your own. It's nice to be able to call that libraries functions in the same convention as your own.
I agree that it's not a huge advantage, which is why I created this thread on the Nim forum recently: https://forum.nim-lang.org/t/4388. My main reason to consider getting rid of it though is people like yourself who dismiss the whole language because of this small feature :(
It has a lot in common with Nim, but is backed by a much larger community. Its FFI interface for C functions is faster than...C.
If Julia doesn't fit your use case(s), Rust might also be worth a look. What worries me about Rust is that it looks to me to have an awful lot of boilerplate that detracts from readability.
In practice, even once I was able to compile basic Nim, there were a lot of glibc and other POSIX dependencies for any of Nim's standard libraries, some of which weren't available in ESP-IDF.
That's totally fair, but many of those dependencies aren't available when compiling to JS either, though, so I was surprised. In the end, delving into the code I found that a lot of stuff was gated behind the equivalent of "platform=JS" switches throughout the stdlib. It seemed like adding support for another platform would be a major pain.
This was all preliminary investigation and I didn't reach out to the Nim community, so perhaps I missed something. Maybe this kind of portability isn't even a goal for Nim. It would definitely be nice to have, though.
Here are some libraries for embedded you can get inspiration from[1]:
- msp430f5510 - Run Nim on MSP430f5510 micro-controller (6KB of RAM).[2]
- stm32f3 - Run Nim on STM32F3 micro-controller (16KB of RAM).[3]
- ardunimo - Nim wrapper for Arduino + LinkIt ONE SDK by Mediatek.[4]
- ardunimesp - Nim wrapper for Arduino ESP8266 framework + A tool for flash, compile and make the nim project into an Arduino project.[5]
[1] https://github.com/VPashkov/awesome-nim#embedded
[2] https://gitlab.com/jalexander8717/msp430f5510-nim
[3] https://github.com/mwbrown/nim_stm32f3
[4] https://github.com/gokr/ardunimo
[5] https://gitlab.com/NetaLabTek/Arduimesp
Also, yes abstracting a platform is a lot of pain but Nim accepts PR even for niche platforms like Haiku, Genode and Nintendo Switch.
Also with the upcoming destructors, the standard library will be GC-free so much more will be usable on embedded.
--gc:stack
--header:"nim.h"
--os:standalone
--noMain
--genDeps
In the end I got Nim code to run and was able to do a "hello world". As a hack I also used -d:android to trigger some conditionals that reduced the reliance on various dependencies. But it wasn't enough to be usable, and it was a bit unfortunate to have to do that.Looking at those projects you linked though, clearly I didn't try hard enough! Great to see that people have had success.
Not always a great thing. Features like implicit syntax rewriting macros or case-insensitive variable names are... interesting experiments in a language, but ultimately not good ideas.
I’ve tried the language a few times over the years, and each time the compiler was just as buggy.
Lots of corner cases to worry about with features, like generators working only if the code that invokes them can entirely be inlined.
ADT are case objects: they are widely used.
https://nim-lang.org/docs/tut2.html#object-oriented-programm...
There are some macros for easier generation: https://github.com/andreaferretti/patty#constructing-variant...
There are pattern matching libs: pattern matching is implemented on top of metaprogramming.
General pattern matching:
the original https://github.com/andreaferretti/patty https://github.com/alehander42/gara (disclaimer: my lib)
patty and gara should eventually combine into a single lib: including probably the current gara matching DSL(inspired by patty) and patty's variant DSL in the same place.
AST pattern matching:
optimized for macros, nice error reporting https://github.com/krux02/ast-pattern-matching
I'm not 100% sure, but I would guess yes.
> Do you see this eventually becoming part of the language?
I can answer this with some confidence: no. Nim is designed to have a small but very extensible core via metaprogramming. This is why we encourage users to write such macros. They may end up in our stdlib eventually but it's unlikely they'll be part of the language.
However it should be possible for sum types/enums: `case` already does it
I'm not too familiar with the specifics of Nim, but how are case-insensitive variable names a bad idea, except that notion is so unfamiliar to programmers?
Flipped around, if a language were to be designed today in a vacuum, what is the argument in favor of case-sensitivity in variable names?
My feeling is that we take case-sensitivity for granted because the vast majority of languages inherited that behavior from history's lineage of great programming languages. But re-evaluated without that history, case-sensitivity seems only to create potential confusion at best and opportunities for trolling and obfuscation at worst. Today's use case to indicate scope, mutability, or permissions is an artifact of this history. As programmers, we know how to read something like this in Java, but if we were able to completely erase our historical knowledge and consider language design without that baggage, this seems like bad language design:
// Local variable square is instantiated equal to
// SQUARE constant in class Square.
Square square = Square.SQUARE;
I'm not sure what alternative syntax we'd have today if we had never adopted case sensitivity in names to begin with, but my hunch is I would prefer it even if it involved more typing (e.g., prefix or suffix symbols).Admittedly, a statically analyzed language will have most case typographical mistakes found and corrected at compile time, so they are a lesser annoyance than case-sensitivity in other contexts (such as filenames or column names in database tables). Generally speaking, I would be in favor of a hard break to drop case-sensitivity across the board, but I'll put that on the same list as adopting the metric system in the US (titled: "LOL, good luck.").
* Simplifies refactoring tools.
* Encourages a consistent style in the ecosystem from the beginning.
* Not needing specialized tooling to grep for an identifier in a codebase.
* Being able to Google a particular function name.
* Consistency in identifier naming when looking at a stacktrace.
How? we would just have case insensitive refactoring tools.
> Encourages a consistent style in the ecosystem from the beginning.
Python, C, Java, etc are case sensitive langs, and it's possible to define function names in all upper-case and do all kind of inconsistent styling. Python has pep8 for a reason.
> Not needing specialized tooling to grep for an identifier in a codebase.
If all langs were case-insensitive, all those tools would be case-insensitive by default (or we would all know the right combination of flags to make them behave).
> Being able to Google a particular function name.
Is google case sensitive? In any case the lang would define a preferred style, like all case-sensitive lang do anyway.
> Consistency in identifier naming when looking at a stacktrace.
Same as above.
Parent said "if a language were to be designed today in a vacuum", I don't think any of your arguments hold.
Does this mean the fastest developer (first to submit a solution?) or does it mean the fastest program in terms of runtime performance or something completely different?
Yes, it means the fastest player on Nim leaderboard, i.e. the one who will on the top of the leaderbord on December 25th.
I guess the fastest program(s) could fall under "the solutions which best showcase the power and capabilities of Nim language".
We have a private leaderboard going at work, but IIRC the limit is 200 users on a board.
If you haven't tried it yet, give AoC a try this year. It's actually really fun!