# A critique of Nim

## Forewords

This is an attempt to provide a constructive critique about the language from 
the experience of a new user. As such, Nim veterans should try to read it with 
some empathy and Nim novices should not take any statement as a source of 
truth. The core team should not think this is a personal critique to them, I 
have the utmost respect for the job you've done and I write this hoping that it 
can be of help.

Moreover English is not my mother tongue, I've done my best to proof read it 
but I apologize in advance for any spelling or phrases that might be hard to 
understand. Please ask for clarifications if something is not clear before 
jumping to conclusions.

## Language

### First impression

Nim has a distinct feeling to it from many other _upcoming languages_ , it 
promises efficiency via static typing while targeting the expressiveness of 
dynamically typed languages, all this without cornering the user into a safe 
subset that excels at a given domain but suffers for everything else. That's a 
great feature on itself, a language that's general enough that can serve you 
well for years to come no matter what the task at hand is.

That generality however goes against the current trend of domain specialization 
in many languages, but to be honest, unless a language already has a killer app 
(i.e. Ruby on Rails) I think it's best to opt for that generality. I think this 
should be more emphasized on the website.

As its distinctive traits there is of course the _significant whitespace 
syntax_ that will attract some people and disgust others, nothing can be done 
about it since it's a very subjective topic. Then there is the whole 
_meta-programming_ machinery, that while impressive at the technical level is a 
double edge sword for a language, some people will immediately discard it while 
others will love it and probably most of them simply won't know what is it good 
for.

Unlike with the syntax however there are objective reasons to look with 
suspicion at a language with _meta-programming_ as a core feature, code is read 
much more than it's written and on many cases _meta-programming_ becomes an 
addictive joy when writing code, unfortunately others will have to read it 
later, maybe even yourself when the time passes. That's why I think that any 
language that offers these capabilities must make them gradual (i.e. generics 
vs templates vs macros), have awesome reporting/debugging stories for it and 
most importantly offer always runtime alternatives to the _meta-programing_ so 
the language is useable and productive without them.

### On boarding experience

Once you start with the language, the first 5 minutes feel great, if you're 
familiar to the _significant whitespace syntax_ it makes you feel at home and 
with a promise of great performance! But then the illusion breaks, it's a typed 
language after all so the compiler starts behaving like your worst enemy. 
That's normal and expected, nothing wrong with the language, the on boarding 
experience though should be tuned for this crucial moment, preparing the user 
for that feeling. The compiler should help a bit, try to add an `int` and a 
`float`, suddenly you're exposed to a zillion overloads of the `+` operator all 
repeating the same message, you might not even know that you can override 
operators in Nim at that point!

Then you learn a bit more, operator overloading seems so cool! You want to try 
by yourself and then you stumble on to something called template, which is a 
_meta-programing_ construct but it's a _simpler version_ of another thing that 
looks even cooler: `macro`. Suddenly you're exposed to a full new world that is 
very attractive and very _ergonomic_ to use, so your instinct is to go down 
that path. Unfortunately while some higher level constructs like generics are 
well defined and understood at large by many users, full blown AST macros is a 
different story. It's a very powerful tool with lots of sharp edges and 
probably only seldom needed. In Nim however you're almost encouraged to use 
them and I think that's a mistake, there is already a great higher level 
general purpose _meta-programming_ tool in the form of `template` (without 
_term rewriting_ ), that should cover most use cases so any reference to real 
macros in the docs should be prefaced with _there will be dragons_. It 's not 
Nim's fault that macros are hard, they are just a very blunt tool at a 
conceptual level and as such any implementation is going to flawed, that's why 
I think that Nim should place some resistance to its use, a pragma could 
probably be enough.

### A fews days in

Anyhow, the main issue with the language is that from the very beginning you're 
confronted with a huge cognitive load that is not easy to handle. Look for 
example at the [Official Tutorial](https://nim-lang.org/docs/tut1.html), the 
surface of the language is too big and while I'm not in a position to argue 
about the need or not of all those features, I think that some of that 
complexity should be initially hidden, offering discoverability paths as the 
user progresses. Let's see some examples:

>   * string formatting needs a better story, there is a new `strformat` module 
> that implements Python's syntax but the user needs to import the module 
> explicitly, however on `system` there are plenty of functions that will be 
> seldom used (i.e. GC_xxx). Moreover it can be used as `fmt""` and `&""` which 
> maybe is convenient in some edge cases but being so simple in Nim to create 
> an alias with a `template`, experienced users can just do that when it makes 
> sense. For me `&` means concatenate not _interpolate_ , if anything `$""` 
> would have been less surprising here but anyway that's syntax bikeshedding 
> which would take us nowhere.
>   * Slicing syntax is a bit surprising, the default `..` is inclusive on both 
> ends ([Dijkstra wouldn't be 
> happy](https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html))
>  and then there is [this 
> pitfall](https://github.com/nim-lang/Nim/issues/6216#issuecomment-321438673). 
> The point is that having a different default from other popular languages is 
> totally fine, you can just learn the new way after a while and then it 
> becomes natural, however it must be rock solid and predictable otherwise your 
> mind is always second guessing itself and it never becomes second nature and 
> frees space in your head for other more important information.
>   * There are too many symbols in `system`. I think it would help to keep the 
> impression of a smaller core language if some of them were moved to specific 
> modules and imported when required.
>   * Imports are... well they don't help. I have a few ideas from previous 
> languages with meta-programing features about how to handle imports more 
> safely, but that ship already sailed I guess. What I think can be done is try 
> to make all the examples in the documentation use the `from module` syntax 
> for explicit importing. Users will end up knowing about `import` and 
> (ab)using it, and it certainly makes writing code simpler if you are all day 
> working on the same code base. But for reading and reviewing code... explicit 
> imports are very helpful.
> 


_Greping_ I think deserves a special mention, features like _smart casing_ , 
_universal call syntax_ and the overloaded operators make the language hard to 
grep. That's a big issue in my opinion, it breaks many work flows a user may 
had already interiorized from other languages, in Nim some generic tooling on 
the command line or built into editors won't always work. I think it's a 
problem that should be acknowledged and reflected explicitly on the docs with 
common workarounds on how to configure editors and such. No amount of custom 
tooling can solve this, people are used to their tools and the language doesn't 
help here unfortunately, you can't have everything I guess.

## Compiler and tooling

The compiler is pretty fast which is a great _feature_. The code is accessible 
although it 's a complex piece of software. I miss some better modularity in 
it, tooling seems to be implemented directly in the compiler and then just a 
façade for the tool. This is fine for a young project with a small user base 
but to scale some refactoring is needed in my opinion. The language ecosystem 
could use some more external projects, that helps with visibility, and also 
allows for competing implementations to iron out issues and establish the best 
solution. It would be ideal if the parser could be fully decoupled from the 
rest of the machinery enabling anyone to create tools for the language just by 
importing a simple module.

Diagnostics are mostly fine from what I've seen so far, although I would 
suggest to have a default mode that is less verbose and more user friendly. The 
example about the `+` overloads for instance is something that should be 
improved, if it's going to print all the overloads the diagnostic should 
collapse them to avoid repeating information, otherwise it's a wall of text 
that scares the user.

I don't have an opinion formed yet about the variety of implementations for GC 
and codegen. Although I could argue that it does increase the cognitive load of 
the language somewhat, too many options can be scary. I guess it's a required 
complexity but I wonder if instrumenting the compiler in a different way would 
help to move some of those implementations to external repositories. That would 
allow different maturity levels on them and decouples release cycles, which is 
more overhead to manage but it'll help to focus the compiler development on a 
reduced set of fully supported variants and allow the different implementations 
to mature independently at their own pace (or rot if not enough people is 
actually interested on it).

As a minor note, the CLI interface feels weird to me, I think I've used the 
`--opt:value` syntax with Mono long ago. I think this just adds a bit to the 
barrier of using the tool easily for no real gain. I would love a new `nimc` 
binary that follows posix best practices for the CLI though.

Any language needs great tooling and in my opinion the more features a language 
has the better tooling it requires. For solo projects is fine but when working 
in a team you need to ensure proper code style and static analysis checks to 
allow code reviews to focus on the task and not on minor details about white 
space preferences. I've seen that there is a recent `nimpretty` tool that 
should work on enforcing a code style, however it's not mature enough for what 
I've seen. This is the kind of project that could really benefit from being 
outside of the main compiler repository, allowing more agile development while 
it matures.

For a language like Nim, with all its meta programming powers, I think that 
there is plenty of room to research new kinds of tooling that allow to keep it 
under control for larger teams.

## StdLib

The initial feeling is that the stdlib is _bateries included_ and that you can 
probably make do without external libraries for most of your tasks. The problem 
is the scope of each module. I do like the idea of having a very complete 
stdlib but it's a huge undertaking to build and maintain one.

Perhaps it's best to focus on a smaller stdlib and prime the ecosystem to 
generate more specialized solutions, bringing then the best in class into the 
stdlib for future versions.

## Documentation

Not pleasant was my initial feeling, although I'm now liking the advantage of 
having the whole manual in a single file in order to quickly search. But I 
think it should be split by default.

The Official Tutorial is a good guide but it's not working as a _tutorial_ , it 
should probably be replaced by the Nim Basics one.

Overall I think some of the complexity aspects I mentioned when talking about 
the language can be palliated with changes in the documentation. I would 
suggest to extract any references to `macro` to a separate document that's 
labeled as advanced and explains the rough edges of the tool so it serves as a 
warning to novices to avoid it and look for other solutions if possible.

## Marketing

In general I find that there is excessive focus on technical details instead of 
language features. For example, I don't really care that there are multiple GC 
implementations to choose from, that's an implementation detail that I'm sure 
is very useful for some use cases but as a first time experience I want to know 
what types of software I can create with Nim and how will it help me creating 
it.

Next the focus should be put on tooling, people expect a variety of tooling at 
their fingerprints from the get go. Packing is taken for granted, it needs to 
advertise the whole set of linting, formatting, debugging and of course cool 
editor and IDE plugins.

I think that kind of marketing is crucial to attract different user profiles, 
and having a diverse user base is very important to make sure that the language 
evolves successfully. It's also a great help to have higher level marketing 
material when you try to introduce a new language at work, every coworker will 
have its favorite _toy language_ so you can't sell it saying that Nim has 
multiple GC implementations, there is probably an obscure language lost with 3 
stars on GitHub that has the coolest GC technology known to man, it's 
impossible to compete in that axis. The language needs to show maturity and 
that's done with great documentation and excellent tooling mostly.

## Community

It's small but active from what I've seen. I'm not much for chats but there 
seems to be a healthy communication channel there.

On GitHub though it doesn't feel very welcoming, some discussions about 
language design tend to get heated on all communities, specially if there is 
syntax involved, which allows everyone to try to contribute. However in pull 
requests I think the project members should make an effort to be more gentle, 
little things like thanking for the contributions even if they are not finally 
accepted can go a long way in bringing new contributors to the project.

I think it'll be great if some easy low-priority issues had instructions, maybe 
it takes the project members longer to write them than to actually fix the 
issue but it's a great way for new contributors to start getting their feet wet.

I understand that some things must have been discussed endless times which must 
be really tiring, and that every new user comes with a bag of brilliant ideas 
that would make the language great, "because now sucks?" I guess is your inner 
reaction. But I think new users bring something vital, a new perspective and 
the necessary feedback loop to know if those features that were carefully 
designed and took away endless hour to implement are actually working or not. 
In my opinion it's the ability of bringing new users what makes evolving a 
language possible, people with different backgrounds and with different 
requirements that will help shape the language and if done properly will in 
turn bring more people to it. 

Reply via email to