TheJach.com

Jach's personal blog

(Largely containing a mind-dump to myselves: past, present, and future)
Current favorite quote: "Supposedly smart people are weirdly ignorant of Bayes' Rule." William B Vogt, 2010

Nim is a cool language

If you haven't heard of Nim, you're missing out. Just google it -- there are some great blogs about why it's amazing. I found another one the other day, and thought it was an interesting topic to continue for myself. That blog is "Nim is the best programming language!" which I don't agree with, but that post is more about what the author looks for in a new language, and what is totally irrelevant. My views differ and yet we both came to love Nim.

I wrote a comment a while back summarizing what I thought are the "hooks" of Nim. The tldr quoting myself a bit is that Nim is fast (at or faster than hand C), supports all three of optional, tunable, implementation-swappable garbage collecting with access to malloc/free, easily embeds C or asm directly, has easy C interfacing, has static typing (mostly downside or me) but usefully inferred so it's often out of the way but also has useful builtin types like sized ints, plus it has distinct types, there are lots of compiler pragmas (common one being inline), user-defined compiler demands to make optimizations via term-rewriting macros, flexible name and calling semantics so that people can use their style preferences even when using third party libs, and finally Nim has an awareness of and support for many higher level language features enjoyed by other languages plus uses its not-exactly-Haskell-level static typing smartly unlike some languages that begin with g and end with o.

OK, so that's really the things that drew me into Nim, but the blog I linked earlier lays it out in a different manner of explicit non-criteria and criteria for whether he looks into a language or not. Let's do the same.

Non criteria


IDE Support



I agree with the other blog on this, but go further. IDE support is a smell. Technically you don't need an IDE to use Java, but in a modern work environment you do. I think IDEs promote lazy, spaghetti, inefficient, handicapped code practices and are best avoided entirely if possible. If a new language requires a behemoth like Eclipse to use effectively (e.g. Flex) it's ultimately not worth it. If you come with vim/emacs support, though, that is cool, but not necessary.

Weird syntax



After swallowing the Lisp pill, every other syntax is ugly. I don't really care if your non-Lisp does some things differently or perhaps less fashionable -- e.g. significant whitespace, using 'end' instead of braces, using pipe characters in odd places, having fancy sigils, and so on. But I hope that sugar is worthwhile, and if I get the feeling like there's a crap ton of ceremony in just the syntax of your new language I'm not going to like it much.

Memory safety



This isn't a big deal for me. By default, if I want memory safety, I have several garbage collected languages on hand that will serve me, some of them faster than others, some of them dynamic or not, and I can pick whichever one best solves the problem. My current programming interests don't require I give up the GC. If you don't ship with a GC, and want me to manage memory directly, that's also not immediately a deal breaker because I'm always interested in things that could replace C/C++. I still like the idea of Rust, but I think there are few converts who will come to it specifically for the safety reasons. Those people already migrated to Java et al. in the 90s/early 2000s. So you're left with legacy C/C++, and one/both of performance-critical new projects like fancy video games or just low level code touching hardware directly like embedded systems development. For the legacy case, absolutely that code should be replaced, by anything better. Personally I'm glad I'm not responsible for any such thing. For both of those other use cases though, the programmers involved don't generally concern themselves with the Safety issues Rust or a GC solves. It's frightfully common practice in the embedded world, if you ever do actually use a malloc(), to never bother with a free() since your system is just going to power down and reinitialize soon anyway. Game programmers always write their own memory managers instead of using malloc/free directly so again they're not too concerned about those bugs apart from how they might crash the program, but that comes up in development and testing and with experience is easily avoidable in the happy-path case. (Of course a malicious adversary can often find unhappy paths that lead to security issues, but for many games and systems this isn't an issue. For those where it is, absolutely memory safety matters, but that is a very domain specific concern that I don't care about too much when evaluating a new to me language.)

Evaluation model



I don't care much about strict vs. lazy. After enough Clojure I think lazy should be more common, and sometimes even the default, but should remain mixed with strict evaluation instead of trying to go all one way or another.

Macros, Lispiness



I have Lisp and Clojure available, so I don't really care if the new language supports something like macros or dynamic scoping or a condition system. Nim's macros and templates are cool, but not great. Certainly better than C++ templates or C preprocessor 'macros'. I write enough code in languages that aren't Lisp so I can't say I'll reject a new language that is also not Lisp. For really terrible boilerplate I can also just write code in Lisp/Python/Vim commands to generate the boilerplate for me. It's amazing when such features are part of the language but not necessary for my day to day.

Unicode



I'm fine with ascii, really. I'll take default utf-8 strings. Unicode in source is probably a bad idea.

Criteria


Potential Liberator



The programming world is sick. Java and C++ are the biggest diseases, does your language offer the potential to cure us of one or both of them? Count me interested. (Not to say something like Lisp isn't already the best potential liberator, but it's had a very long time to liberate and has failed, possibly because of some deep flaw in the language enthusiasts can't see, or because it requires too much brain power, perhaps it was all just the sad consequence of the free market being satisfied with local optima.)

Higher order functions



Anonymous functions not required but very nice. Closures required, even if not default. First class functions required. Immutable types allowed, better if default. If all you have is "final" that doesn't stop any thread from mucking the contents of a "final" object, you're useless. (Java -- but at least it has concurrent and persistent classes elsewhere.)

Typing



Different from the blog author, I prefer dynamic typing. Unfortunately Python is dynamic enough, and when it's not, there's Clojure, and when that's not enough, there's Lisp which is this weird whatever-you-want-typed thing that one might call "dynamic" only at the risk of putting Lisp's mega powerful type system into the same limited bucket as e.g. JavaScript.

This isn't to say static typing is a deal breaker, but in many cases, it will be. The main utility I see for static types is to generate efficient code. The things you can do and prove with fancier type systems -- let's actually just use the word saner to distinguish things like Java/C/C++/C#/Go from not-those -- are neat but not nearly as useful as higher performance, or even other proofs with different systems like TLA+. So if you're going to require me to pay the ceremony cost, it better be worth it for the performance gains alone. I believe Rust has mostly gotten there, Haskell has not. If you're already dynamic but want to support optional typing, well, there better be benefits beyond documentation or making your bloated autorefactoring IDE a little faster if I'm ever going to try them and I'm going to worry the language's dev effort is wasted if there isn't something real to justify optional typing beyond author preference. You can lessen the penalty I give to static systems with type inference of various power, of course, and especially give me proper generics at the start so I don't have to deal with your nonsense when you somehow become popular without them and then add them later; by now I can actually demand it in the age when even C++ has 'auto' and even Java has the diamond operator and lambdas.

Stronger typing is better, implicit type conversions often suck. Ahem, Java.

If you're going to have static types, I'd like to see dependent types as well. It's sad when Clojure has something like Prismatic (or now core.spec) that is way more powerful than many static type systems. (And Lisp is still out of this world in its type power.)

Open source license



Non-negotiable.

Reach, or Generality



I don't often remember this one, because it's so intuitive. If your language offers me nothing more than existing languages I know or am learning, why would I switch? If it offers me less (go) in one or even several (go) ways than existing languages, why would I switch? I've been happily using Python for a lot of my personal project needs for at least 8 years now, those projects have spanned web sites, video games, FPGAs, database driving, UI apps, shell scripts, scientific computing, and probably some other areas too. Notably not web front end or embedded systems like an ARM chip (at least not without running on an entire Linux distro...). Python has been an incredibly useful and rewarding language. If I had went deep into Lisp, I suspect I'd have had the same experience, but also would have used ecl for embedded and parenscript maybe for front-end. So Lisp passes this criteria. Ruby ultimately failed this criteria since I couldn't see a use for it beyond Rails, and I was (and still sort of am) adamantly against giant web frameworks in the first place. Another benefit of Python is that it works on the main OSes, I can even ship a big EXE to Windows if I need to. Python doesn't require an IDE either so I can code it anywhere.

C also passes this generality test, for obvious reasons. It's literally everywhere.

The one exception is if you have a niche domain specific language, then I might give you a shot if my domain interest happens to overlap. But you better justify why you're not just a set of Lisp macros or something. (SQL could qualify for this as it's a useful language for set operations and I think you lose a little something when you go full-Lispy-DSL on it even if you gain in query manipulation powers.)

REPL



Nim actually fails this (I think they removed an experimental repl even) but this is probably the biggest barrier to me using Nim a lot more. There's no excuse to not have a REPL, even if it isn't as powerful as Lisp's. That's just asking for IDE entryism.

A concurrency story



Honestly I don't write much parallel code, but it's inevitably going to be the future. A new language can't be blind to this problem and act like a new BASIC that is super procedural, one-core-one-thread oriented, and doesn't give a thought to parallelism or more generally concurrency. Even go has a story for this, so there's no excuse not to have a strategy. It's interesting when there are different strategies at play -- Node as the ultimate in async "everything is parallel except your code" design actually worked, cancer that it was at first. Immutable data structures, less memory sharing, and more advanced things like an STM are also useful to get us out of the mutex and deadlock hell of naive threading. What I am still looking for though, and maybe I'll find it in a Nim library, is a focused dedication to local parallelism rather than concurrency in general like Clojure. New desktop-class CPUs have 10 cores, effectively 20 with hyperthreading. Even mobile devices have 4 ARM cores. The new Xeon Phi has 72 cores with 4 threads per core. Multicore is the future, and we need systems and perhaps languages to exploit the hell out of it more than we need systems (which we now more or less have already even if they'll get better once the local parallelism problem is solved better) that scale out horizontally with multiple machines. This will probably have to come out of retiring high performance computing people.

Summary



Nim isn't the best language, Lisp is. But I don't know Lisp that well, and even if I did it'd be quite the battle to convince others to go along with me on it, which is why I still look out for new languages.

For me Nim is the current best replacement for C or C++. It's similar enough to other things I know (if you know C you can start going in Nim right away and ignore many of its nicer features but still have a more pleasant and safe experience than plain C without sacrificing performance), it's similar enough to other things devs in general know. It was designed for the purpose of being just as capable as C which mandates the ability to bypass a runtime/garbage collector and manage memory directly. (Lisp can do this, but I think it's implementation dependent, or you wind up generating lower level code that you pass on to another tool, and anyway the core of Lisp was more like a discovery risen from noticing some neat math as opposed to consciously designed. Design certainly came later on, including disagreements so basic like how best to represent data literals yet nevertheless people can compromise by using Common Lisp and sharing macros.)

For me Nim also is a serious contender for replacing bits of Java, Python, and so on. It's not as buttery smooth as Python, but it's not bad, it's way more pleasant than Java but lacks its huge ecosystem. Nim has enough modern language features most people just expect at this point that when you switch from a high level dynamic language that's not Lispy to Nim it doesn't feel like such a massive downgrade as if you went to Java, Go, or one of the C's. In other words, the developer experience of Nim is surprisingly high for being capable of such tedious things like manual memory management and crazy explicit nested nested nested object typing.


Posted on 2016-06-03 by Jach

Tags: nim, programming

Permalink: https://www.thejach.com/view/id/323

Trackback URL: https://www.thejach.com/view/2016/6/nim_is_a_cool_language

Back to the top

Anonymous May 19, 2017 03:08:30 AM Hello.
As already said, Lisp is a good and powerful language. But it lacks some important things. At least the open-source ones.
a) concurrency:
It was simply nothing to be considered in the past, where Lisp has it's time. And so it lacks severely for it today. SBCL tries to workaround with bordeaux-threads, that apply a bit more than the standard but that's it. GnuLisp does not even have a thread support. So thread support is very poor yet. And the argument "it needs to be concerned in the OS, so we keep it out of Lisp" does simply not cut it. Yes, different OSes support different amounth of thread support. But I have to offer the tools and features in the language, so that the OS is the bottleneck and not the language, to my opinion.
b) GC:
In a lot of open Lisp implementations the GC cannot be switched off. And the Lisp is interpreted. That makes it hard for quickness and realtime requirements.
Some lips support a generation without GC, like GnuLisp, but lacks at multithreading. Others are the other way round.
There are only 2 Lisps that fit the bill for everything: LispWorks, and AllegroLisp. Both are commercial and rather expensive.
That is why I do not see Lisp as the best for everything. In these cases Nim is superior.
Maybe someday we have a full blown open-source Lisp as a standard, but I do not see that yet.
Jach May 19, 2017 09:41:59 PM Yeah it's unfortunate to say the least that the Lisp standard doesn't cover concurrency like it should. But what's wrong with bordeaux-threads? I haven't really compared it with C's pthreads but I imagine it's similar. There's also lparallel on top of that which I would probably use in practice and fset if I want immutable collections. In addition to SBCL, lparallel works for Clozure and ABCL, so that's three open source Lisps.

If you've got just one implementation, thrusting requirements onto the OS to be able to implement you can be fine.. but for Lisp it's important to consider how many weird platforms it was on and what had to be compromised for Common Lisp to be.

What implementation of Common Lisp is interpreted? I'd hesitate to call it Lisp. Note COMPILE is part of the standard...

As mentioned above I don't need to turn the GC off in most of the things I do. I really like that Nim's GC is so configurable but it's not a hard requirement for most people. And in the Lisp world, there's support, even if unfortunately it's not universal. The stories of GOAL for (albeit ancient by modern standards) games and Harlequin Common Lisp (now LispWorks) running on Deep Space 1 (where having a REPL was crucial for fixing a bug 100 million miles away) are evidence that Lisp can be used successfully in very constrained environments, and not in the theoretical "everything is turing complete so you could rewrite all software in lolcode" sense but a real business-value and engineering-value sense. And with something more modern, I shudder to think what this product would look like under the hood if it was made in C++ instead of Lisp...

Neither LispWorks nor AllegroLisp are very expensive in the bigger scheme of things. LispWorks 64bit is $3k/user for Professional, $4500 if Enterprise. Cheaper if you only need 32bit, but come on. Anyway, it's one time. Then an upgrade fee of $750/$1125 respectively, when and if you need the upgrade. Contrast to IntelliJ IDEA, which a lot of companies shell out for, and it is just an IDE. They now charge $500/user for the first year, then $400, then $300 indefinitely. Cheaper for sure but not by much, and I wonder what the 15 year numbers would look like for LispWorks. Will JetBrains even be around in 15 years? If you're in the business of games you're going to pay a lot more for various other middleware and assets and so forth. A lot of products still somehow get away with charging per-cpu-core.

As an interested hobbyist with a fairly average full time SE salary, getting the LispWorks Enterprise Edition would cost me a little over one paycheck after tax. I'm ok with SBCL and vim right now for my tinkering though. I can do things with that setup that I simply can't with other languages, Nim included. I think when Nim gets a quality REPL, that's when I'll get really excited for it again.

Nim is/would be my first choice for a lot of things. It might even be the best case for some, or at least better than Lisp, but that doesn't make it best overall. Overall bestness is what I mean when I say Lisp is best, not that Lisp is best at whatever criteria you care to name. Lisp really sucks on the criteria of selling the use of it to management, which is a big criteria for me that's captured by my "Potential Liberator" section. Nim on the other hand doesn't seem like it'd be a hard sell to a group steeped in any one of C++ or Python or Go. Easier than Rust I would bet. It's just got a lot of benefits with little weirdness. I'm hoping Kotlin will end up being a similar thing for those of us stuck deep in JavaLand where selling Clojure is impossible, even with Clojure having been quite successful on that criteria already.
Jach May 19, 2017 11:33:26 PM Heck, I should have just linked to the wiki entry on concurrency: http://www.cliki.net/Concurrency. There's a lot of options.
Back to the first comment

Comment using the form below

(Only if you want to be notified of further responses, never displayed.)

Your Comment:

LaTeX allowed in comments, use $$\$\$...\$\$$$ to wrap inline and $$[math]...[/math]$$ to wrap blocks.