NOTE: nim was previously known as nimrod.
A quick look at nim programming language
This is a short intro to a fun, productive language called nim.
nim is a new programming language developed by Andreas Rumph.
nim is a statically typed, compiled langauge.
What kind of language is it?
There are few things more irriting than a programming language or library webpage that doesn’t show how the syntax/typical usage looks like. So, let’s see how nim code looks like:
# compute average line length
var count = 0
var sum = 0
for line in stdin.lines:
count += 1
sum += line.len
echo "Average line length: ",
if count > 0: sum / count else: 0
The var
keyword is familiar if you have used C#
, which essentially
means that the variable count has an implicit type. That is, you don’t
have to explicitly mention the type, in this case an integer
.
The for
loop line reads naturally for the Python programmer, including
the :
starting a new block.
nim is inspired by many popular languages old and new. It is quite easy to read and understand what is going on.
According to Araq
, the creator of Nim:
Macro system inspired by Lisp.
Export marker taken from Oberon.
Argument passing semantics taken from Ada.
distinct types inspired by Ada.
Syntax also heavily influenced by Python.
Generics inspired by C++.
The 3 pointer like types ptr, ref, var taken from Modula 3.
async / await stolen from C#.
let taken from ML.
...
One could also argue that Nim is a statically typed Lisp with an
infix syntax extended by essential features necessary for
systems programming. And dumbed down to support efficient
compilation to C (where are my continuations?!)..
How to install it?
Checkout the code from github. (Note these steps are outdated since the language was renamed from nimrod
to nim
).
$ git clone git://github.com/Araq/Nimrod.git
$ cd Nimrod
$ git clone --depth 1 git://github.com/nimrod-code/csources
$ cd csources && ./build.sh
$ cd ..
$ bin/nimrod c koch
$ ./koch boot -d:release
$ ./kock install $HOME/nimrod
This installs nimrod under $HOME/nimrod
. Add $HOME/nimrod/bin
to the
$PATH
.
What is the performance like?
Nim compiles to C, so you can expect C-like performance (hard performance numbers are a game of benchmarks and nobody ain’t got no time for that…). To go with Nim’s authors’ own word “it is performant for ‘soft real time’ systems like games”. Take it as you may.
For me, Nim is a vast upgrade over dynamic languages like Python and Ruby without sacrificing a whole lot of expressiveness.
What are some of the quirks
By looking at examples and reading applications/libs written in Nim
you would not guess it, but nim is case insensitive. That is
var hello
and var HELLO
and var HeLlo
are treated as the same by
the compiler. Where nim takes it one step ahead is it’s treatment of
underscores, which means var H_E_l_lo
is same as var hello
. This
might appear crazy, but this removes the entire bikeshed argument about
which style of variable naming is best.
The nim programming community has it’s own best practices for
variable names, for instance, new types are declared as TFoo
an
inspiration from Pascal.
A quick tour of the command line
To compile a nim file foo.nim
, you type
nim c foo.nim
which will give you a foo
executable in the same directory, assuming
there were no compilation errors. You will also see a nimcache
directory.
This directory contains the intermediary C
files generated.
What are the resources to learn more
The website: http://www.nim-lang.org The forum: http://forum.nim-lang.org — this is probably the best place to get your questions answered.
How can I use nim?
For a young language like nim has not managed to corner itself into any one niche.
nim has been used to write: (Update, Aug 2024: See Awesome Nim and nimble.directory for a list of libraries for nim - a lot more diverse than you think!)
Personally, I have been using nim to replace a few of command line python applications that process large amounts of data.
Standard libraries
Nim’s standard library has XML, CSV, Web, String, Regular expressions, OS, Database drivers, Network programming and more…. It also has wrappers to popular C libraries like X, GTK, pcre, cairo, sdl, OpenGL etc.,. nim libraries have you covered for day-to-day programming needs.
Web development
Editors and IDEs
Emacs has a nim-mode. A nim specific IDE called Aporia built using
the GTK toolkit. I haven’t used it much beyond installing it because I
prefer using the Emacs nim-mode.
Aug 2024: I use zed, and it uses LSP.
Packaging and reusing libraries
nim libraries can be installed using the nimble package manager. The nim
After successful compilation, copy the babel executable to a directory in
your $PATH
. I put it under $HOME/nim/bin
along with other nimrod
executables.
Distributing applications written in nim
A .nimble
file is required at the root of your application source
directory. Take for example the jester.nimble
file that is part of the
Jester web framework distribution” —
[Package]
name = "jester"
version = "0.1.0"
author = "Dominik Picheta"
description = "A sinatra-like web framework for Nimrod."
license = "MIT"
SkipFiles = "todo.markdown"
The file is self explanatory. Once you have the nimble file in the directory, running nimble build
will build binaries.
Cross Compilation
From SO
I was having the same issue getting nim to compile executables for Windows, from a GNU/Linux machine, so I made a bash script. It takes the path to the directory containing
*.nim
source files and the name of the executable file to output. I’m sure you could swap out the GCC compiler (MinGW in this case) and change the--os: switch
as appropriate:
#!/usr/bin/env bash
# Nim must generate C sources only, to be fed to MingW
nim c --cpu:amd64 --os:windows --opt:speed --embedsrc --threads:on --checks:on -c -d:release $1/*.nim
# Copy nimbase.h so MingW32 can find it during compilation and linking
cp /opt/Nim/lib/nimbase.h $1/nimcache/nimbase.h
mkdir -p $1/bin
cd $1/nimcache && x86_64-w64-mingw32-gcc -save-temps $1/nimcache/*.c -o $1/bin/$2.exe
rm $1/nimcache/*.{i,s} # only care about *.o objects
ls -lAhF $1/nimcache
ls -lAhF $1/bin
Concepts
Memory Model
Related
Links
-
Choosing Nim out of a crowded market for systems programming languages - Nim forum; Nov 2022.
-
Metaprogramming abilities, article by Andreas Rumpf, creator of Nim.
-
Debugging Nim [forum thread
-
Discussion on GC in light of Gradha’s decision to “leave”
-
What makes nim practical — how to use
niminst
among other things. -
Introduction to Metaprogramming in Nim · HookRace - a Nim blog for now
-
Speeding Up Python with Nim; Jan 2019.
-
DNA Alignment with Python, Nim and JavaScript - Boolean Biotech
-
Markdown Parser in Nim; Oct 2018.
-
Why I enjoy using the Nim programming language at Reddit. : RedditEng; Nov 2022.
Books
Tutorials
Interop (FFI)
- (2) Exporting C APIs from Zig, D, and Nim - YouTube
- Subset Park: Nim for Python Programmers
- PyVideo.org · Nim: A New Option for Optimizing Inner Loops
Testing
Interesting projects in Nim
- Fusion OS, an OS written in Nim.
Compile time
Libraries
- nimongo — study for the async, asyncdispatch methods.
- nim-appkit — github org around various useful nim libs; eg:
nim-yaml
. - genotrance/nimgen: Nimgen is a helper for c2nim to simpilfy and automate the wrapping of C libraries
- FedericoCeratto/nim_project_maker: Initialize a Nim project directory
- Patty - A pattern matching library | patty
- citycide/cascade - Method & assignment cascades for Nim, inspired by Smalltalk & Dart.
- zero-functional/zero-functional: A library providing zero-cost chaining for functional abstractions in Nim.
- Pebaz/nimporter: Compile Nim Extensions for Python On Import!
Web development
- planety/prologue: Powerful and flexible web framework written in Nim (This is the framework I used for developing tohray. )
- An older (2019) but useful article about Nim tutorial for setting up a website.
- Mummy is a multi-threaded HTTP 1.1 and WebSocket server. Used to write cxplanner website. Production Use of Mummy, and another
Dev tools
Dev Notes
Upgrade nim to the latest version using choosenim:
$ choosenim update stable
# downloaded, built and installed 2.0.8 (Aug 2, 2024)
$ which nim
/Users/pradeep/.nimble/bin/nim
$ nim -v
Nim Compiler Version 2.0.8 [MacOSX: amd64]
...
Random notes
Nim’s GC is per thread: You can easily have threads which are hard realtime and don’t use the GC and happily use the GC in the other threads… And the type and effect system ensures you don’t mix these different per thread heaps. — Araq
[Nim has] C++-like features (generic types, operator overloading, function overloading, inline functions, optional O-O, optional data hiding, C pointers, bitwise-compatibility with C, your choice of manual memory management or GC) and C++-like run-time speed, combined with Python-like syntax and compile-time Lisp-like macros. — HN comment
The no GC future is
--gc:stack
which replaces the GC with memory regions. So any library that uses the GC really uses a memory region. The entire situation is ok. Note that the GC is always thread local anyway, so you can also just run realtime threads with other threads that use the stdlib and thus the GC. — Araq
use these:
alloc, dealloc, allocShared, deallocShared
. You can cast the pointer from and toptr
types and then you have your manually managed heap memory. (This is a general answer to how to use non-GC’d memory and has nothing to do with memory regions) — flyx
A good example of using FFI in nim in comparison to D and rust.
Discussion in /r/python
about a nim article.
From a reddit thread: What problem does Nim solve?
Low friction, high performance development.
It feels like a scripting language in terms of writing code rapidly. Deferred GC means no need to worry about memory (although easy to manually manage it). So, a sort of statically typed, compiled Python with C-level speed and tiny, self contained executables.
High level AST metaprogramming that lets you hygienically extend the language, has static typing with inference and uses structural typing for generics which often means you don’t even need object hierarchies, extensive compile time evaluation (to the point you can process files, create hash maps or pull web data by simply defining your desired variables as ‘const’).
It compiles to C, C++, LLVM (experimental), and Javascript. Excellent FFI so you can use all the C, C++ and JS libraries as well as Nim ones. Very fast compilation that’s going to get faster from what I’ve read about future plans.
This may be subjective but I also like how clean it looks. Few $!;@ symbols and easy to read.
Personally, it’s brought the fun back into development for me.
Stack vs GC vs Manual memory management
type Foo = object
→ Value type, on the stacktype Foo = ref object
→ Ref type, managed by GCtype Foo = ptr object
→ Pointer type, manual memory management (malloc, free, memcopy …)- Pointer arithmetic can be achieved using casts. via
Stack vs Heap from a Reddit thread:
By PMunch
:
First order of business heap vs. stack: Whenever you call a function it creates a stack frame. Stacks are simply said first in last out, so when a function call is done it pops it’s frame from the stack, and you return to the previous frame. When you declare a variable inside a scope in is typically allocated on the stack. This means that when the function returns that part of memory is not in use anymore and the value is gone (technically it’s still there, but we shouldn’t try to access it). The heap on the other hand is quite a bit different. Items on the heap are allocated and live there until you deallocate them. In C this is done manually with calls like malloc and free. In Nim however, and C# for that matter, we have the garbage collector (or GC), this nifty thing reads through our memory and checks if anything still points to things allocated on the heap and if nothing is pointing to that which was previously allocated it gets free’d. This means that we won’t leak memory as easily as you would in C as things that we’ve lost every reference to get’s automatically cleaned. Okay, now that we know where our stuff lives in memory, let’s look at how Nim represents those things. Basic types are things like integers, floats, bools, and characters. Their size is known and static. Nim also calls strings a basic type, but those are not quite like the rest since they can change in size. Since our stack is first in last out it means that it makes sense to store everything in order. But storing things in order isn’t possible if you want to change the size of something (for example appending to a string). So when we create numbers in our Nim code they will be stored on the stack, this is why you never need to free your numbers and don’t have to set them to nil when you’re done with them. So what are types? In Nim you can create aliases for types like type age = int this is just a way to say that age is an integer, and it will be treated like one for all intents and purposes. If we want to create collections of types to represent something particular we can create objects, don’t think of these quite like objects in an object oriented language, think of them more like structs in C. Such objects are simply a collection of values. So in Nim when we create an object it will live with us on the stack. If we return an object it will be copied to our caller, and if we insert it into a data structure it will also be copied. While this might be practical in many cases (even offering a speed benefit if done right) we often don’t want to copy our large objects around. This is when allocating on the heap comes into play. If we define our type as a ref object it means that the type is actually a reference to an object. I’ll come back to the difference between a reference and a pointer later, but for now just remember that a reference is the memory location of an object on the heap. This means that if we return a ref object it means that we’re only returning the memory address of that object, not copying the object itself. This also means that if we insert it into a data structure only the reference to the object is inserted. Whenever you see new SomeObject it means that it allocates memory for that object on the heap, and gives us a reference to this object. If we had simply done var myObject: SomeObject and SomeObject was defined as a ref object we would only have a reference on our stack, so trying to access it would crash saying we had an “Illegal storage access”. This is because Nim defaults our value to nil, and no-one is allowed to access memory area 0. So imagine we had an object that contained the height, the weight, and the age of a person. That could be represented by three integers. If we wanted to return this object it would mean copying all those three values to our caller. If we defined it as a reference, we would only pass one integer, the position in memory (on the heap) where we stored the three others. Conveniently this also means that if one functions modifies a value in a referenced object, that change would be visible for all other functions using the same reference (since they all point to the same place in memory). This is practical for example if you want to make one list sorted by age, one by height, and the third by weight. Instead of copying our person three times, we could just use three references, one in each list. So now that we know where and how are values are stored we can look at the difference between a pointer and a reference. In pure Nim code you would typically only use references, these are what every call to new creates and what goes on behind the scenes most of the time when working with strings. A reference is also called a managed pointer, it simply means that Nim manages this area of memory, and that it will be automatically free’d for us by the garbage collector when Nim sees that we’re not using it any longer. Pointers on the other hand are unmanaged meaning that Nim doesn’t try to do anything with the memory behind that pointer, most of the time you won’t even know what is there. The reason there are pointers in Nim is mostly to interface with C. In C every time you want objects on the heap you need to manually malloc and free them. Many C libraries work by passing around a pointer to a structure containing some state and all good libraries have some way of dealing with this memory. Typically you do it by calling some initialisation function when you begin and then some cleanup function when you are done. In Nim we might want to use a C library such as that, but since we might loose the reference in our own code while the library still keeps a reference somewhere which Nim doesn’t know about we can’t have a reference to it as Nim would garbage collect it. So instead we have pointers. I hope this made it somewhat clearer, if you want more examples or if anything was unclear, feel free to ask.
Benchmarks
- basic benchmarks against Crystal, D, Go, Julia, and other compiled langs.
Talks
- Dominik Picheta at NIDevCon, June 2017
- C++ as Assembly 2.0 - Hello Nim from Open Fest Bulgaria; Dec 2019.
- FOSDEM 2020 - Async await in Nim by Dominik Picheta.
- “Nim Nuggets: Systems Programming & Metaprogramming Magic” by Aditya Siram at Strange Loop 2021. The slides are here.
Editors
Visual studio Code
Nimlime
Nimlime is a plugin for Sublime Text the provides Nim compatibility. This is my custom settings overriding the default settings:
{
"nim.executable": "/Users/pgowda/src/Nim/bin/nim",
"nimble.executable": "/Users/pgowda/.nimble/bin/nimble",
"nimsuggest.executable": "/Users/pgowda/.nimble/bin/nimsuggest",
"provide_immediate_nim_completions": true,
"output.send": false
}
The output.send
option set to false
is an important one; withtout this,
the plugin would create a new compiler output buffer everytime the file is
built, which is everytime I saved the file!.
Blogs
Code
Code to read etc.,
- def-/nim-unsorted: Unsorted Nim code that I wrote for some reason (largely Rosetta Code)
- generating bindings to a C library (libarchive) in this case — https://github.com/genotrance/nimarchive/blob/master/nimarchive.cfg this is pretty fascinating.
- krux02/opengl-sandbox: just a macro that generates the fuzz you need to deal with OpenGL
Eventually I decided, that being able to do transformations on the AST (abstract syntax tree) of the programming language would greatly help to develop this toolbox. I tried out Rust, but I quickly found out that their macros are too weak to even implement println, and I had to look somewhere else. (At this point in time Rust also has procedural macros, but they still don’t appear to be powerful enough to do the things I need to do for this project). Eventually with Nim I finally found a language that suited my needs. It has AST based macros, arbitrary code execution at compile time, static typing, reflection and a lot more.
News
See also: Crystal, Interesting Programming Languages, D, Python, Rust.