Skip to content
Please note that GitHub no longer supports Internet Explorer.

We recommend upgrading to the latest Microsoft Edge, Google Chrome, or Firefox.

Learn more

Nim for Python Programmers

Juan Carlos edited this page Feb 2, 2020 · 226 revisions

Table Of Contents

Comparison Objects Do I have to know C? Named Tuples
Variables self.__init__() Strings Lists
Imports Ranges String Operations List Comprehensions
Arrays Slices Tuples Dict Comprehensions
Set Comprehensions Reading and writing files Decorators Lambdas
Sets JSON Map & Filter Optional Indentation
Dictionaries CamelCase DocStrings Import Nim files on Python
Ternary operators Unittests def Vs proc / func Self-Execution of Main Module
Python Syntax for Nim Publish to PYPI Silent Compilation Compiler Help
Build Modes Abstract Base Classes Decorators Philosophy
Templates Nim running Interpreted Nim on the browser Standard Library Equivalents
Arduino, MicroPython, CircuitPython LiveCoding, FoxDot, SuperCollider How to share variables between functions?
DISCLAIMER!

Unofficial, work in progress! This is still a stub. Please help extending it. There may be inaccuracies in this guide. This is a guide for people with experience in Python or a similar language. The guide assumes some intermediate knowledge.

The general tutorials can be found here:
http://nim-lang.org/docs/tut1.html
http://nim-lang.org/docs/tut2.html

The manual provides an overview of the language: http://nim-lang.org/docs/manual.html

Comparison

Feature :snake: Python :crown: Nim
Execution model Virtual Machine, JIT (Interpreter) Machine code via C/C++ (Compiler)
Written using C Nim
License Python Software Foundation License MIT
Version (Mayor) 3.x 1.x
Meta-programming :heavy_check_mark: metaclass, eval :heavy_check_mark: template, macro
Memory Management Garbage collector Garbage collector or manual
Typing Dynamic Static
Dependent types :negative_squared_cross_mark: :heavy_check_mark: Partial support
Generics Duck typing :heavy_check_mark:
int8/16/32/64 types :negative_squared_cross_mark: :heavy_check_mark:
float32/float64 types :negative_squared_cross_mark: :heavy_check_mark:
Char types :negative_squared_cross_mark: :heavy_check_mark:
Subrange types :negative_squared_cross_mark: :heavy_check_mark:
Enum types :heavy_check_mark: :heavy_check_mark:
Bigints (arbitrary size) :heavy_check_mark: :heavy_check_mark: via Nimble
Biggest Integer Std Lib Unknown? 18_446_744_073_709_551_615, uint64() type
Arrays :heavy_check_mark: :heavy_check_mark:
Type inference Duck typing :heavy_check_mark:
Closures :heavy_check_mark: :heavy_check_mark:
Operator Overloading :heavy_check_mark: :heavy_check_mark: on any type
Custom Operators :negative_squared_cross_mark: :heavy_check_mark:
Object-Oriented :heavy_check_mark: :heavy_check_mark:
Methods :heavy_check_mark: :heavy_check_mark:
Exceptions :heavy_check_mark: :heavy_check_mark:
Anonymous Functions :heavy_check_mark: single-line only :heavy_check_mark: multi-line
List Comprehensions :heavy_check_mark: :heavy_check_mark:
Dict Comprehensions :heavy_check_mark: :heavy_check_mark:
Set Comprehensions :heavy_check_mark: :heavy_check_mark:
Custom Object Comprehensions :negative_squared_cross_mark: :heavy_check_mark:
Immutability Limited (frozenset, tuple, etc) :heavy_check_mark:
Function Arguments Immutability Mutable Immutable
Formatted String Literals :heavy_check_mark: F-Strings :heavy_check_mark: strformat
FFI :heavy_check_mark: CTypes (C only) :heavy_check_mark: C/C++/JS
Async :heavy_check_mark: :heavy_check_mark:
Threads :heavy_check_mark: (Global Interpreter Lock) :heavy_check_mark:
Regex :heavy_check_mark: Not Perl-compatible :heavy_check_mark: Perl Compatible Regular Expressions
Self-Documentation comments :heavy_check_mark: plain-text multi-line strings :heavy_check_mark: ReStructuredText/Markdown
Package Publishing :heavy_check_mark: not built-in, requires twine :heavy_check_mark: built-in, nimble
Package Manager :heavy_check_mark: pip :heavy_check_mark: nimble
Code AutoFormatter :heavy_check_mark: black via PIP :heavy_check_mark: nimpretty built-in
File extensions .py, .pyi, .pyd, .pyo, .pyw, .pyz, .pyx .nim, .nims
Temporary Intermediate Representation format .pyc C
Uses SheBang on files :heavy_check_mark: :negative_squared_cross_mark:
Indentation Tabs and Spaces Spaces

Variables

Creating a new variable uses var or let or const. Nim has immutability and compile-time function execution. You can assign functions to variables.

Declaration Compile-Time Run-Time Immutable Requires Assignment
var :negative_squared_cross_mark: :heavy_check_mark: :negative_squared_cross_mark: :negative_squared_cross_mark:
let :negative_squared_cross_mark: :heavy_check_mark: :heavy_check_mark: :heavy_check_mark:
const :heavy_check_mark: :negative_squared_cross_mark: :heavy_check_mark: :heavy_check_mark:

If you are just starting from scratch, you can use var while learning, it will not produce an error for doing so, until you learn more.

Imports

Import :snake: Python :crown: Nim
Only 1 symbol, use unqualified from math import sin from math import sin
All symbols, use unqualified from math import * import math (recommended)
All symbols, use fully qualified import math (recommended) from math import nil
All symbols except 1, use unqualified :negative_squared_cross_mark: import math except sin

In Nim, import math imports all the symbols from the math module (sin(), cos(), etc) so that they can be used unqualified. The Python equivalent is from math import *.

If you prefer not to import all the symbols, and instead to use qualified names, the Nim code is from math import nil. Then you can call math.sin() etc. The Python equivalent is import math.

The reasons that it is generally safe to import all names in Nim are that the compiler will not actually include any unused functions (so there's no overhead), and because Nim is statically typed it can usually distinguish between two imported functions with the same names based on the types of the arguments they are called with. And in the rare cases where the types are the same, you can still fully qualify the name to disambiguate.

Nim can use the imports on the same line. From Python to Nim as minimum as possible example:

import foo
import bar
import baz

:arrow_up: Python :arrow_up:          :arrow_down: Nim :arrow_down:

import foo, bar, baz

If your line gets too long, imports can also be an indented block:

import 
  foo, bar, baz, more, imports, 
  here, we, split, multiple, lines

If you like 1 import per line, because of Git diffs, imports can also be 1 per line:

import 
  foo, 
  bar, 
  baz

If you are just starting from scratch, you can use imports like Python while learning, it will not produce an error for doing so, until you learn more.

For better understanding, is recommended to read: https://narimiran.github.io/2019/07/01/nim-import.html

Arrays

Arrays are fixed size, start at index 0 and can contain elements of the same type.

When passing an array to a function in Nim, the argument is an immutable reference. Nim will include run-time checks on the bounds of the arrays.

You can use an openarray to accept an array of any size on the function arguments, and you can use low(your_array) and high(your_array) to query the bounds of the array.

Objects

Objects in Nim behave quite differently from classes in Python. Objects support inheritance and OOP. Classes are named Types on Nim. Functions are free floating functions, not bound to objects (however, you can use them in a very similar way to Python), you can call a function on objects with the Object.function(). Nim does not have an implicit self nor this.

Imagine it like functions get "glued" to the objects at compile-time, then you can use it at run-time as if it was Python class and methods.

From Python to Nim as minimum as possible example:

class Kitten(object):
    """ Documentation Here """

    def purr(self):
        print("Purr Purr")

Kitten().purr()

:arrow_up: Python :arrow_up:          :arrow_down: Nim :arrow_down:

type Kitten = object  ## Documentation Here
proc purr(self: Kitten) = echo "Purr Purr"
Kitten().purr()

Python-like object orientation example:

type Animal = ref object of RootObj ## Animal base object.
  age: int                          
  name: string                      ## Attributes of base object.

type Cat = ref object of Animal     ## Cat inherited object.
  playfulness: float                ## Attributes of inherited object.

func increase_age(self: Cat) =
  self.age.inc()                    # Cat object function, access and *modify* object.

var kitten = Cat(name: "Tom")       # Cat object instance.
kitten.increase_age()               # Cat object function used.

assert kitten.name == "Tom"         # Assert on Cat object.
assert kitten.age == 1

self.__init__()

After the Cat example probably you are wondering how to do def __init__(self, arg):.

Python __init__() is Nim newObject() or initObject(), lets make an __init__() for the Cat:

type Cat = object                # Cat object.
  age: int                          
  name: string                   # Attributes of Cat object.

func newCat(age = 2): Cat =      # Cat.__init__(self, age=2)                    
  result.age = age               # self.age = age         
  result.name = "adopt_me"       # self.name = "adopt_me" 

var kitten = newCat()            # Cat object instance.

assert kitten.name == "adopt_me" # Assert on Cat object.
assert kitten.age == 2

Naming is a convention and best practice, when you want init for Foo just make newFoo() or initFoo(). As you may notice newCat is just a function that returns a Cat.

Read the documentation for Naming things following conventions and best practices.

Ranges

In Python, simple integer for loops use the range generator function. For the 1- and 2- argument forms of this function, nim's .. iterator works almost the same way:

for i in 0..10:
  echo i  # Prints 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

for i in 5..10:
  echo i  # Prints 5, 6, 7, 8, 9, 10

Note that the .. operator includes the end of the range, whereas Python's range(a, b) does not include b. If you prefer this behavior, use the ..< iterator instead:

for i in 0..<10:
  echo i  # Prints 0, 1, 2, 3, 4, 5, 6, 7, 8, 9

Python range() also has an optional third parameter, which is the value to increment by each step, which can be positive or negative. If you need this behavior, use the countup or countdown iterators:

for i in countup(1, 10, 2):
  echo i  # Prints 1, 3, 5, 7, 9

Slices

The syntax for slice ranges is different. Python a[x:y] is Nim a[x..<y].

let variable = [1, 2, 3, 4]
assert variable[0..0] == @[1]
assert variable[0..1] == @[1, 2]
assert variable[0..<2] == @[1, 2]
assert variable[0..3] == @[1, 2, 3, 4]

Strings

Lang String Multi-line string Raw String Multi-line Raw string Quote Always Unicode
:snake: Python "foo" """foo""" r"foo" r"""foo""" " ' :negative_squared_cross_mark:
:crown: Nim "foo" """foo""" r"foo" r"""foo""" " :heavy_check_mark:

String Ops

Ops :snake: Python :crown: Nim
Lower "ABCD".lower() "ABCD".toLowerAscii()
Strip " ab ".strip() " ab ".strip()
Split "a,b,c".split(",") "a,b,c".split(",")
Concatenation "a" + "b" "a" & "b"
Find "abcd".find("c") "abcd".find("c")
Starts With "abc".startswith("ab") "abc".startswith("ab")
Ends With "abc".endswith("ab") "abc".endswith("ab")
Split Lines "1\n2\n3".splitlines() "1\n2\n3".splitlines()
Slicing "abcd"[0:2] "abcd"[0..<2]
Slicing 1 char "abcd"[2] "abcd"[2]
Reverse Slicing "abcd"[-1] "abcd"[^1]
Normalize unicodedata.normalize("NFC", "Foo") "Foo".normalize()
Count Lines len("1\n2\n3".splitlines()) "1\n2\n3".countLines()
Repeat "foo" * 9 "foo".repeat(9)
Indent textwrap.indent("foo", " " * 9) "a".indent(9)
Unindent textwrap.dedent("foo") :question: "foo".unindent(9)
Parse Bool bool(distutils.util.strtobool("fALse")) :question: parseBool("fALse")
Parse Int int("42") parseInt("42")
Parse Float float("3.14") parseFloat("3.14")
Formatted String Literals f"foo {1 + 2} bar {variable}" fmt"foo {1 + 2} bar {variable}"
Levenshtein distance :negative_squared_cross_mark: editDistance("Kitten", "Bitten")

Standard Library Equivalents

Use :snake: Python :crown: Nim
Operating System os os
String operations string strutils
Date & time datetime times
Random random random
Regular expressions re re
HTTP urllib httpclient
Logging logging logging
Run external commands subprocess osproc
Path manipulation pathlib, os.path os
Mathematic math, cmath math
MIME Types mimetypes mimetypes
SQLite SQL sqlite3 db_sqlite
Postgres SQL :negative_squared_cross_mark: db_postgres
Serialization pickle json, marshal
Base64 base64 base64
Open web browser URL webbrowser browsers
Async asyncio asyncdispatch, asyncfile, asyncnet, asyncstreams
Unittests unittests unittest
Diff difflib diff
Colors colorsys colors
MD5 hashlib.md5 md5
SHA1 hashlib.sha1 sha1
HTTP Server http.server asynchttpserver
Lexer shlex lexbase
Multi-Threading threading threadpool
URL & URI urllib.parse uri
CSV csv parsecsv
Parse command line arguments argparse parseopt
SMTP smtplib smtp
HTTP Cookies http.cookies cookies
Statistics statistics stats
Text wrapping textwrap wordwrap
Windows Registry winreg registry
POSIX posix posix, posix_utils
SSL ssl openssl
CGI cgi cgi
Parse JSON json parsejson, json
Parse INI configparser parsecfg
Parse XML xml parsexml, xmltree
Parse HTML html.parser htmlparser
Parse SQL :negative_squared_cross_mark: parsesql
Colors on the Terminal :negative_squared_cross_mark: terminal
Linux Distro Detection :negative_squared_cross_mark: distros
HTML Generator :negative_squared_cross_mark: htmlgen
Syntax Sugar :negative_squared_cross_mark: sugar
JavaScript & Frontend :negative_squared_cross_mark: dom, asyncjs, jscore, jsffi

Tuples

Anonymous Tuple

(1, 2, 3)

:arrow_up: Python :arrow_up:          :arrow_down: Nim :arrow_down:

(1, 2, 3)

Named Tuple

  • Keys named, no Tuple name. Python NamedTuple requires import collections.
collections.namedtuple("_", "key0 key1")("foo", 42)

:arrow_up: Python :arrow_up:          :arrow_down: Nim :arrow_down:

(key0: "foo", key1: 42)

Named Tuple

  • Keys named, Tuple named. Python NamedTuple requires import collections.
collections.namedtuple("NameHere", "key0 key1")("foo", 42)

:arrow_up: Python :arrow_up:          :arrow_down: Nim :arrow_down:

type NameHere = tuple[key0: string, key1: int]
var variable: NameHere = (key0: "foo", key1: 42)

Nim Tuples are a lot like Python NamedTuple in that the tuple members have names. Nim Tuples can have mixed types.

See manual for a more in depth look at tuples.

Lists

Nim sequences are not fixed size, can grow and shrink, start at index 0 and can contain elements of the same type.

["foo", "bar", "baz"]

:arrow_up: Python :arrow_up:          :arrow_down: Nim :arrow_down:

@["foo", "bar", "baz"]

List Comprehensions

variable = [item for item in (-9, 1, 42, 0, -1, 9)]

:arrow_up: Python :arrow_up:          :arrow_down: Nim :arrow_down:

let variable = collect(newSeq):
  for item in @[-9, 1, 42, 0, -1, 9]: item
  • Whats collect()?.

collect() takes as argument whatever your returning type uses as constructor.

Dict Comprehensions

variable = {key: value for key, value in enumerate((-9, 1, 42, 0, -1, 9))}

:arrow_up: Python :arrow_up:          :arrow_down: Nim :arrow_down:

let variable = collect(initTable(4)):
  for key, value in @[-9, 1, 42, 0, -1, 9]: {key: value}

Set Comprehensions

variable = {item for item in (-9, 1, 42, 0, -1, 9)}

:arrow_up: Python :arrow_up:          :arrow_down: Nim :arrow_down:

let variable = collect(initHashSet):
  for item in @[-9, 1, 42, 0, -1, 9]: {item}

Sets

Python sets are not like Nim set type. If the values that will go in the set are known beforehand and finite, you can create an Enum for them. Otherwise you can emulate a Python set using a HashSet. The Nim set type is faster and memory-efficient. In fact, set is implemented with a bit vector, whereas HashSet is implemented as a dictionary. For simple flag types and small mathematical sets, use set.

Dictionaries

Use Tables for Python dicts.

Lang Dictionary Ordered Dictionary Counter Imports
:snake: Python dict() OrderedDict() Counter() import collections
:crown: Nim Table() OrderedTable() CountTable() import tables

Table Constructors

dict(key="value", other="things")

:arrow_up: Python :arrow_up:          :arrow_down: Nim :arrow_down:

to_table({"key": "value", "other": "things"})

Ordered Dictionary

collections.OrderedDict([(8, "hp"), (4, "laser"), (9, "engine")])

:arrow_up: Python :arrow_up:          :arrow_down: Nim :arrow_down:

to_ordered_table({8: "hp", 4: "laser", 9: "engine"})

Counters

collections.Counter(["a", "b", "c", "a", "b", "b"])

:arrow_up: Python :arrow_up:          :arrow_down: Nim :arrow_down:

to_count_table("abcabb")

Examples:

import tables

var dictionary = to_table({"hi": 1, "there": 2})

assert dictionary["hi"] == 1
dictionary["hi"] = 42
assert dictionary["hi"] == 42

assert len(dictionary) == 2
assert dictionary.has_key("hi")

for key, value in dictionary:
  echo key, value

Ternary operators

"result0" if conditional else "result1"

:arrow_up: Python :arrow_up:          :arrow_down: Nim :arrow_down:

if conditional: "result0" else: "result1"

You probably notice that the Ternary Operator is just an if..else inline.

Reading and writing files

Reading files line by line

with open("yourfile.txt", "r") as f:
    for line in f:
        print(line)

:arrow_up: Python :arrow_up:          :arrow_down: Nim :arrow_down:

for line in lines("yourfile.txt"):
  echo line

Read and write files:

write_file("yourfile.txt", "this string simulates data")
assert read_file("yourfile.txt") == "this string simulates data"

Read files at compile-time:

const constant = static_read("yourfile.txt")  # Returns a string at compile-time

Map & Filter

def isPositive(arg: int) -> bool: 
  return arg > 0

map(isPositive, [1, 2,-3, 5, -9])
filter(isPositive, [1, 2,-3, 5, -9])

:arrow_up: Python :arrow_up:          :arrow_down: Nim :arrow_down:

proc isPositive(arg: int): bool = 
  return arg > 0 

echo map([1, 2,-3, 5, -9], isPositive)
echo filter([1, 2,-3, 5, -9], isPositive)

Lambdas

variable: typing.Callable[[int, int], int] = lambda var1, var2: var1 + var2

:arrow_up: Python :arrow_up:          :arrow_down: Nim :arrow_down:

var variable = (proc (var1, var2: int): int = var1 + var2)

Anonymous Functions on Nim are basically a function without a name and surrounded by parens.

Decorators

  • Templates and Macros can be used similar to Python Decorators.
def decorator(argument):
  print("This is a Decorator") 
  return argument

@decorator
def function_with_decorator() -> int:
  return 42

print(function_with_decorator())

:arrow_up: Python :arrow_up:          :arrow_down: Nim :arrow_down:

template decorator(argument: untyped) =
  echo "This mimics a Decorator"
  argument

func function_with_decorator(): int {.decorator.} =
  return 42

echo function_with_decorator()
  • Why Nim wont use @decorator syntax?.

Nim uses {. and .} because it can have several decorators together.

Also Nim one works on variables and types:

func function_with_decorator(): int {.discardable, inline, compiletime.} =
  return 42

let variable {.compiletime.} = 1000 / 2

type Colors {.pure.} = enum Red, Green, Blue

JSON

Python uses multi-line strings with JSON inside, Nim uses literal JSON directly on the code.

import json

variable = """{
    "key": "value",
    "other": true
}"""
variable = json.loads(variable)
print(variable)

:arrow_up: Python :arrow_up:          :arrow_down: Nim :arrow_down:

import json

var variable = %*{
  "key": "value",
  "other": true
}
echo variable
  • %* converts everything inside the braces to JSON, JSON has a type JsonNode.
  • %* can have variables and literals inside the braces.
  • JSON can have comments inside the braces of %*, Nim kind of comments.
  • If the JSON is not valid JSON the code will not compile.
  • JsonNode can be useful on Nim because is a type that can have mixed types and grow/shrink.
  • You can read JSON at compile-time, and store it on a constant as string.
  • To parse JSON from string you can use parseJson("{}").
  • To parse JSON from a file parseFile("file.json").
  • JSON documentation

Self-Execution of Main Module

if __name__ == "__main__":
  main()

:arrow_up: Python :arrow_up:          :arrow_down: Nim :arrow_down:

when is_main_module:
  main()

Unittests

import unittest

def setUpModule():
    """Setup: Run once before all tests."""
    pass

def tearDownModule():
    """Teardown: Run once after all tests."""
    pass


class TestName(unittest.TestCase):

    """Test Name"""

    def setUp(self):
        """Setup: Run once before each tests."""
        pass

    def tearDown(self):
        """Teardown: Run once after each test."""
        pass

    def test_example(self):
        self.assertEqual(42, 42)


if __name__ == "__main__":
    unittest.main()

:arrow_up: Python :arrow_up:          :arrow_down: Nim :arrow_down:

import unittest

suite "Test Name":

  echo "Setup: Run once before all tests."

  setup:
    echo "Setup: Run once before each test."

  teardown:
    echo "Teardown: Run once after each test."

  test "example":
    assert 42 == 42

  echo "Teardown: Run once after all tests."

DocStrings

DocStrings on Nim are ReSTructuredText and MarkDown comments starting with ##, notice ReSTructuredText and MarkDown can be mixed together if you want.

Generate HTML, Latex (PDF) and JSON documentation from source code with nim doc file.nim.

Nim can generate a dependency graph DOT .dot file with nim genDepend file.nim.

You can run documentation as Unittests with runnableExamples.

""" Documentation of Module """

class Kitten(object):
    """ Documentation of Class """
    age: int

    def purr(self):
        """ Documentation of function """
        print("Purr Purr")

:arrow_up: Python :arrow_up:          :arrow_down: Nim :arrow_down:

## Documentation of Module *ReSTructuredText* and **MarkDown**

type Kitten = object ## Documentation of Type *ReSTructuredText* and **MarkDown**
  age: int  ## Documentation of Attribute *ReSTructuredText* and **MarkDown**

proc purr(self: Kitten) =
  ## Documentation of Function *ReSTructuredText* and **MarkDown**
  echo "Purr Purr"

Optional Indentation

For short lines you can write the code on a single line, inline constructs without errors or warnings.

let a = try: 1 + 2 except: 42 finally: echo "Inline Try"

let b = if true: 2 / 4 elif false: 4 * 2 else: 0

for i in 0..9: echo i

proc foo() = echo "Function"

(proc   () = echo "Anonimous Function")()

template bar() = echo "Template"

macro baz() = echo "Macro"

var i = 0
while i < 9: i += 1

when is_main_module: echo 42

CamelCase

  • Why Nim is CamelCase instead of snake_case?.

It really isn't, Nim is Style Agnostic.

let camelCase = 42      # Declaring as camelCase 
assert camel_case == 42 # Using as snake_case

let snake_case = 1      # Declaring as snake_case
assert snakeCase == 1   # Using as camelCase

This feature allows Nim to seamlessly interoperate with a lot of programming languages with different styles.

For more homogeneous code a default is chosen, and can even be enforced if you want, to enforce the default code style you can add to the compile command --styleCheck:hint, Nim will style check your code before compilation, similar to pycodestyle on Python, if you want even more strict style you can use --styleCheck:error.

A lot of programming languages have some kind of Case Insensitivity, such as: PowerShell, SQL, PHP, Lisp, Assembly, Batch, ABAP, Ada, Visual Basic, VB.NET, Fortran, Pascal, Forth, Cobol, Scheme, Red, Rebol.

If you are just starting from scratch, you can use Python like names while learning, it will not produce an error for doing so, until you learn more.

def Vs proc/func

  • Why Nim wont use def instead of proc?.

Nim is uses proc for normal functions from "Procedure" naming.

Uses func for Functional Programming side-effect free functions like from "mathematical functions".

Nim has side-effects tracking.

  • Why Nim wont use async def?.

Async is just a macro on Nim, no need to change the syntax of the language, is like a Decorator on Python.

Also on Nim the same function can be Async and Sync at the same time, with the same code, with the same name.

If you are just starting from scratch, you can use proc for all the functions while learning, it will not produce an error for doing so, until you learn more.

Do I have to know C?

You never have to actually manually edit C, the same way on Python you never manually edit the PYC files.

On Nim you code by writing Nim, the same way on Python you code writing Python.

Templates

Templates replace its invocation with its content body at compile-time.

Imagine it like the compiler will copy&paste a chunk of code for you.

Template allows to have a function-like constructs without any overheads, allows you to split huge functions into smaller constructs.

Too much function names and variable names may pollute and saturate the local namespace, variables inside templates do not exist outside of its template, templates do not exist on the namespace at run-time (if you do not export it), templates may optimize certain values if they are known at compile-time.

Templates allows you to implement a very high-level beautiful API for everyday usage, while keeping the low-level optimized stuff out of your head and DRY.

Python with open("file.txt", mode = "r") as file: implemented using 1 template:

Template explanation animation

GIF is not perfect, but a lazy simplified approximation!.

This is not the way to read files on Nim, just an exercise.

Template is not perfect, but a lazy approximation!, exercise for the reader to try to improve it ;P

template with_open(name: string, mode: char, body: untyped) =
  let flag = if mode == 'w': fmWrite else: fmRead  # "flag" dont exist outside of this template
  let file {.inject.} = open(name, flag)   # Create and inject "file" variable, "file" variable exists outside of this template because {.inject.}
  body                                     # "body" is code passed as argument
  file.close()                             # Code after code passed as argument

with_open("testin.nim", 'r'): # Mimic Python with open("file", mode='r') as file
  echo "Hello Templates"      # Code inside the template, this 2 lines are "body" argument on the template
  echo file.read_all()        # This line uses "file" variable

If you are just starting from scratch, do not worry, you can use Functions for everything while learning.

How to share variables between functions?

Sharing variables between functions is similar to Python.

Global variable:

global_variable = ""

def function0():
    global_variable = "cat"

def function1():
    global_variable = "dog"

function0()
assert global_variable == "cat"
function1()
assert global_variable == "dog"
function0()
assert global_variable == "cat"

:arrow_up: Python :arrow_up:          :arrow_down: Nim :arrow_down:

var global_variable = ""

proc function0() =
  global_variable = "cat"

proc function1() =
  global_variable = "dog"

function0()
assert global_variable == "cat"
function1()
assert global_variable == "dog"
function0()
assert global_variable == "cat"

Object Attribute:

class IceCream(object):
  object_attribute: int

def functiona(food: IceCream):
    food.object_attribute = 9

def functionb(food: IceCream):
    food.object_attribute = 5

food = IceCream()
functiona(food)
assert food.object_attribute == 9
functionb(food)
assert food.object_attribute == 5
functiona(food)
assert food.object_attribute == 9

:arrow_up: Python :arrow_up:          :arrow_down: Nim :arrow_down:

type IceCream = object
  object_attribute: int

proc functiona(food: var IceCream) =
  food.object_attribute = 9

proc functionb(food: var IceCream) =
  food.object_attribute = 5

var food = IceCream()
functiona(food)
assert food.object_attribute == 9
functionb(food)
assert food.object_attribute == 5
functiona(food)
assert food.object_attribute == 9

You can pass functions as argument of functions like in Python too.

Import Nim files on Python

Python Syntax for Nim

Publish to PYPI

Silent Compilation

If you want the compilation to be completely silent (you may miss important warnings and hints), you can add to the compile command --hints:off --verbosity:0.

Compiler Help

The Compiler help is long, to make it more user friendly only the most frequent commands are shown with --help, if you want to see the full help you can use --fullhelp.

Build Modes

When your code is ready for production you should use a Release build, you can add to the compile command -d:release.

Feature Release Build Debug Build
Speed Fast Slow
File Size Small Big
Optimized :heavy_check_mark: :negative_squared_cross_mark:
Tracebacks :negative_squared_cross_mark: :heavy_check_mark:
Run-time checks :heavy_check_mark: :heavy_check_mark:
Compile-time checks :heavy_check_mark: :heavy_check_mark:
assert :negative_squared_cross_mark: :heavy_check_mark:
doAssert :heavy_check_mark: :heavy_check_mark:

MicroPython

Nim compiles to C, so it can run on Arduino and similar hardware.

Has several memory management strategies to fit your needs, including full manual memory management. Nim binaries are small when built for Release and it can fit the hardware tiny storage.

SuperCollider

SuperCollider is C++ so it can be re-utilized using Nim.

Theoretically, Nim SuperCollider plugins should be just as fast as C code. Nim metaprogramming allows to build LiveCoding friendly DSLs.

Some projects for Nim LiveCoding:

ABC

See this

Philosophy

The key to understanding Nim is that Nim was designed to be as fast as C, but to be much safer. Many of the design-decisions are based on making it harder to shoot yourself in the foot. In Python there are no pointers (everything is treated as a reference). While Nim does give you pointers, Nim gives you other, safer tools for your everyday needs, while pointers are mostly reserved for interfacing with C and doing low-level system programming.

Contrarily to Python, most Nim code can be executed at compile time to perform meta-programming. You can do a lot of the DSLs possible with Python decorators / metaprogramming with Nim macros and pragmas. (And some stuff that you can't!). Of course this requires some different patterns and more type safety.

:arrow_up: :arrow_up: :arrow_up: :arrow_up:

Clone this wiki locally
You can’t perform that action at this time.