Patten Matching in Nim
nim-lang.orgI played with Nim a little bit, loving it for a while, and I quickly ran into the same issue I had with Julia, which was that I had trouble staying in the basic language. Both Nim and Julia's documentation, and many modules/imports/includes/whatever of those languages, immediately jump to "holy shit we have macros! Importing this module changes the syntax! Isn't that awesome?!" And I'm like, no, it's not awesome. New syntax, new syntactic sugar, etc. is a learning curve every time for me. I'm often skeptical of operator overloading in C++ and python, so macro crazy languages are even worse. I feel like I must be in the minority, or I haven't hit that programming nirvana.
...except that macros don't change the syntax of the language!
They just offer convenience on top of it, most common example is the `=>` lambda operator from the `sugar` module. I do agree, that the pattern matching macro presented in the article is a bit hard to get used to, but you don't have to, if you don't like pattern matching. And of course there are plenty of alternatives available as well, the simplest one imo is https://github.com/andreaferretti/patty
In all non-trivial codebases you have to learn how other people implemented something. It can be easy or take time depending on the how well it's written.
Macros are not different than functions: one can create readable or crazy spaghetti code in any language.
If you find a codebase full of unreadable macros it's not different than any other type of bad code: stay away from it or simplify it.
Personally, I'm yet to find a macro that makes the code less readable or more difficult to understand.
Yes! I'm constantly seeing people enthuse about how awesome macros are, and done well they're great, but I don't want every library I use to have a whole new set of syntax.
While I don't claim to have a comprehensive numbers, I did a quick `rg` on a full clone of nimble package list, and found that macros constitute approximately 0.4% of all definitions (functions, procedures, templates, methods).
You would be surprised how much can be accomplished just using simpler language constructs - even for the complex operations. Manual for example advises to use the least powerful construct for the job https://nim-lang.org/docs/manual.html#macros-caseminusof-mac...proc: 401673 macro: 1869 template: 16255 func: 8746 converter: 3538 iterator: 1833 method: 9025
Also - notable portion of the commonly used macros is for embedded DSLs that in other languages would require you to use a separate build step with some external tool, or give way to horrible clutches like operator overloading-based DSL.> Style note: For code readability, it is the best idea to use the least powerful programming construct that still suffices. So the "check list" is: > Use an ordinary proc/iterator, if possible. > Else: Use a generic proc/iterator, if possible. > Else: Use a template, if possible. > Else: Use a macro.Note that I did the full clone several months back, and it is missing around two hundred packages that were added in this time, but I doubt it changes numbers greatly.
While that may be true, my experience was that you immediately see folks assuming you're using the 'sugar' macros, and then I went to do something with JSON and that was macros as well. Honestly curious, while .4% of all definitions are macros, how many things are using those macros? That's probably hard to tell, but I feel like that would be closer to representing the need for learning all the macros. I hear you, though, that I might have been nearing the end of the steep part of the learning curve.
Sometimes endowing existing syntax with richer semantics requires almost no documentation because things "just work". For example, cligen [1] uses a macro to synthesize an argv parser -> call dispatch CLI system. It could almost have been a template but it needs to loop over parameters more than once (and then grew a lot of features).
Anyway, since Nim has user-defined new operators, using those might also bother some. E.g., there could be a proc `=>` meaning something very different from that sugar module macro of the same name. Really, these work just like other procs modularity-wise. The syntax/grammar does not actually change, though. It's just flexible in the first place.
These complaints sound more like excuses to me. Every language has calls, styles, idioms, etc. you need to learn as a client of whatever it is you're calling/doing. And every language has quirky syntax corners. E.g., many are unaware that C lets you say "(a,b,c,d)" { which evaluates but ignores the result of "a,b,c" }. Yet you still often hear people proclaim how simple C syntax is. "Compared to what?", I guess (e.g. C++).
I do get where you are coming from "overloading"-wise - not just operators, but also regular procs (which in Nim are the same...there is just a proc `<`). When learning an alien language/code-base/libraries it really helps to have jump-to-definition. Nim via nimsuggest has great support for this if you configure your editor (vim, emacs, VisualStudio Code, etc.). You may have skipped that step but might love it more if you did it next time. nimsuggest can be aware of local and global --path modifications and the type context of the jump-to-definition. So, it can do a much better job than, say, ctags. (and yes, nimsuggest works for all the various definition types like macros, templates, etc., even the ones named like an operator such as `<`).
I totally agree with you, that's why I started Nimlings to help new Nim engineers get familiar with the language. Let's face it, you probably won't need macros WELL into your Nim lifecycle.
The amount of power that Nim has is absolutely obscene. I don't really have a good use-case for it right now, but it's a language that I know I'd enjoy if I got into it.
Does Nim solve the "what color is your function" problem?
That's kind of not a language-level thing. I mean it is, but it's tied very very closely to runtime choices rather than the language itself.
Basically your question is equivalent to asking whether the language has green threads. The original article describing color (http://journal.stuffwithstuff.com/2015/02/01/what-color-is-y...) says it's just threads, but it glosses over why a language like C# decided to add async-await and why Java now has its ongoing Loom project.
System threads are heavyweight and as a result usually unsuitable as your concurrency primitive for highly concurrent applications (although you can and most runtimes do build on top of threads). And so if you only have that, you inevitably get what the article calls the "color problem" (see e.g. in Java the async IO APIs, which the article laments, but doesn't explore why they exist and moreover are often preferred by Java programmers over their synchronous equivalents), because system threads aren't enough and a color-based solution inevitably must appear on top of them.
So the question to ask is "does Nim have green threads?" To which the answer is no (and then by extension it does have the color problem, in this case via async-await).
Nim attempts to at least partially resolve this via the {.multisync.} pragma. It's functionally equivalent to {.async.} but means that the compiler also generates code for synchronous IO, this is what enables the stdlib to implement an HttpClient and AsyncHttpClient with little to no duplication.
Of course, the downside is that it relies on library authors to use this mechanism.
Fwiw I personally quite like having function colors for async tasks and I/O, and in fact wish we also had them for purity (both read-purity and write-purity).
> The await keyword can only be used inside procedures marked with the {.async.} pragma.
Note also that using async/await should be used with the new, non-default ORC garbage collector.
Exciting! I look forward to trying it out. Having macros in Nim really does make it so much easier to extend. The writeup on writing a macro based DSL in Nim seems handy. Mostly I use the `template` form of simplified macros that cover most meta programming needs along with `const` and compile time execution. Nim has a full VM, so almost any codecan run at compile time.
There's also a new library for 2d graphics being written and figma like UIs: https://forum.nim-lang.org/t/7559 I'm hoping items like the DSL help people write more interesting Nim libraries.
The pattern matching blog post is indeed great (good job @haxscramper!). Casual Nim observers might want to click on the Blog link at the top (Or all the other links!). There have been quite a few posts in the past year or so on ARC & ORC garbage collection, multi-threading run-times, and a variety of "overview posts" on a lot of various happenings.
NIM seems like this unicorn of a language of having great performance, amazing balance between being both high-level and low-level, extremely approachable due to syntax ... yet very few have actual discovered and use NIM.
I wonder why.
I tried out Nim. My first reaction was that I absolutely loved it. Second reaction was when I needed to look up how to do something and I realised that the docs aren't amazing. Third reaction was when I first ran into a problem I couldn't find anything on Google, and had to post on the Nim forums. The forums were great but there's an inevitable time lag between posting and getting a response.
Then I started looking at Rust. For my purposes, first reaction to Rust was that I hated it. Borrow checker, lifetimes, all the stuff everyone complains about. But once I got over the initial bump it ticked all of the above boxes that Nim didn't, and I ended up appreciating the guardrails it provides around safety that Nim doesn't (or didn't, a lot has happened with garbage collection etc. since I last looked).
If I had more free time in life I'd absolutely be making side projects in Nim.
> I ended up appreciating the guardrails it provides around safety that Nim doesn't...
fwiw this is plain wrong, a garbage collected language in general is memory safe, just with a different scheme (a garbage collector) than rust's memory model. Both are better options from one that is unmanaged.
I hate to attract negative attention from rust fanboys. But none likes Rust's borrow checker and hopefully language developers can come up with a better design.
What exactly is wrong with Nim's docs, in your opinion? They seem fine after perusing the ones at the following link a bit just now:
IMO, while the descriptive department is passable, the docs seriously lack on the prescriptive side. What I found extremely helpful when dabbling with Rust, is that every module in STD docs has a pretty comprehensive general description and usage suggestions, often accompanied by some examples. It really works great lifting the curse of knowledge for the uninitiated and it doesn't get in the way when the reader already knows what he's after as thse introductions are easily skippable.
> The forums were great but there's an inevitable time lag between posting and getting a response.
FWIW the core devs and many others are present on the #nim channel on Freenode with bridges to Discord, Gitter, and (non-Gitter) Matrix.
Many people find just searching the big index [1] to be enough. Besides the Forum and the IRC (often more immediate), there is also a Wiki [2] where you can maybe contribute documentation for "what you would have liked to have seen" and numerous other documentation things off of [3].
It's true the "just web search it" approach may be weaker than other programming languages. That's a late stage network effect (as is lower latency of Forum/IRC responses).
[1] https://nim-lang.org/docs/theindex.html
Easy: no killer app nor big corps support.
According to Wikipedia it started in 2008 and is still very alive. It's a good sign.
I think network effects have a lot to do with it. If a language has a small ecosystem, it's less attractive for new developers, so the ecosystem stays small. That said, I think the Nim ecosystem might be able to achieve a critical mass of useful libraries soon and break out of that cycle. There's been a lot of good work done in the UI and web domains in the past year, which is such a large market that even capturing a small portion would bring a lot of developers into the ecosystem.
In my case, I actually prefer my languages to be more opinionated. Nim implements just about every paradigm it can think of, and if there are multiple ways to implement a paradigm, it implements them all. It feels like a sophomore college student who keeps switching majors.
Some people prefer "There is Only One Way to Do It" languages.
Some people prefer "There is More Than One Way to Do It" languages.
Nim is more like a "There Are Seven Ways to Do It and the Eighth Way is Planned" language.
Nim seems pretty opinionated to me: use imperative style, minimal or none OOP (preference for macro built DSLs over OOP), functional allowed for those who like it.
Also looking code around I think style and usage is pretty consistent and Nim idiomatic code is certainly a thing. Overall I think it is a definitely consistent language. It is definitely evolving but you can go a long (loong way) with 1.0 features.
I also believe “there is only on way to do it” is more of a slogan/goal than a real thing.
Nim is a very flexible language where things that would, in almost any other language (besides Lisp/Racket/etc) require direct compiler support (like async/coloring) can be done as libraries.
Consequently, people disagreeing the way they do in their opinions, even if there were not seven ways to do something in the core language/stdlib, libraries would add those ways. So, the ecosystem would still have them all and more (eventually).
Personal opinion, but I would say Nim is remarkable in being able to support & manage all this diversity. One does not see Nim style guides like C++ style guides where you are only supposed to use the 5..10% or whatever of the specified language that everyone on a team understands, for example. { Yet! Famous last words... :-) }
> Nim implements just about every paradigm it can think of, and if there are multiple ways to implement a paradigm, it implements them all.
any examples?
The first one most people run into: Nim is roughly as performant as Rust, but it's easier to learn because it's garbage collected. But it's also not garbage collected, if you prefer (with caveats). Want a different flavor of garbage collected? We have three (with caveats)!
thats not a different way of doing same thing, its doing different thing. this not even an argument, if language can provide multiple memory management strategies, in what world is that a bad thing?
and if you somehow confused about choice (of gc), you should stick to default.
also, there is no such thing as "There is Only One Way to Do It" languages. your entire argument is just a circlejerk point.
Is Nim garbage collected? There is no simple answer to that.
When it is garbage collected, how is it garbage collected? There is no simple answer to that.
As stated in my original comment, I prefer languages which are more opinionated than that. Nim is a Swiss Army knife; some people like that, but I prefer choosing a fixed-blade knife to suit my current purpose.
Is C garbage collected? There is no simple answer to that. Link with Boehm (or something similar) and it is. These things are often (with almost any language) aspects of the implementation & deployment, and most mature languages have multiple answers as well. Is Python "compiled"? Well, with Pypy or Cython or etc. it is. (Or maybe it's always at least "compiled" to byte codes run by a slow byte code interpreter.) Etc., etc. I think your standards for "simplicity" or univalence may be, as @SolitudeSF alluded to, unsatisfied by even prog.lang's you like (which you gave no concrete examples of).
> Is Nim garbage collected? There is no simple answer to that.
Yes, Nim v1 is a garbage collected language. It also shipped with custom destructors support https://nim-lang.github.io/Nim/destructors.html.
Nim v2, which is hopefully two releases away, will ship with a memory management scheme called "orc" https://nim-lang.org/blog/2020/12/08/introducing-orc.html
However right now, it is recommended that all new code is written with the `--gc:orc` switch.
I'm jumping in as soon as a robust 3D game engine uses it as a first class language.
...because nowadays the world of software is marketing driven. People often ask me what big company is behind Nim even before asking about its design and features.
It's written "Nim"
One of my knocks against it is using white space for scoping/blocks a la Python.
While it may be great for beginners or for short scripts, over time it becomes a real pain when refactoring. Editors mess up indentation all the time when copying and pasting. You only have to inadvertently make a statement either incorrectly part of, or not part of an if a few times, before you become leery of that choice.
I've been coding for 14 years and I've never had a "whitespace" problem. This just seems like a nitpick from nitpickers.
Coded in C#, python, go, ruby, elixir, nim, rust, javascript, typescript. Not once felt this.
I run into this a lot with Python, where when I manually cut/paste some block, I have to go up and double check all the lines are in the right place, etc. because an autoformatter can't do it correctly and obviously like it can in C++, e.g.
Whitespace as syntax is a somewhat controversial choice. Others will talk about missing semi-colon bugs or other delimiter-not-matching-whitespace bugs. Their counter "You only have to mis-indent an if relative to semi-colons once to be leery of that choice."
Most of the time (more often than Python), you can use parentheses if you really want. Getting everyone else on your team to stick to that formatting convention..maybe not so easy because, well, not everyone shares your opinion/values. :-)
Personally, I'm skeptical of the "great for beginners" point. I've helped a few beginners in Python who had subtle, yet program-breaking bugs just because their return statement was indented to the wrong level. It would have been easier to just use a brace-delimited language and run the autoformatter to reveal any discrepancies.
(Or we could be using a lisp, but that's another problem :) )
very well done, looking forward to use it!
related discussion in nim forum (started because I had question about fusion): https://forum.nim-lang.org/t/7608
if this keeps staying in front page, maybe it makes sense for @dang to fix the typo in the title (Patten -> Pattern)
s/Patten/Pattern/
Pattern matching to the rescue!
@Dang Yes, that would be nice! Could you, please?
As an Elixir developer who uses Nim for love of the language this is very exciting!
Using nim pattern matching library to implement simple dataflow programming DSL.
Great article!