index 日本語

Internet of Tomohiro

If you found my articles interesting or useful, please donate using following links:


Frequently Asked Questions about Nim programming language

This is a FAQ about Nim programming language. If you have a question about Nim, please ask it in Nim forum.

  1. Language design
    1. What is Zen of Nim?
    2. Why is Nim style insensitive?
    3. Why Nim use space for indent instead of tab?
    4. Why Nim use garbage collecter?
    5. Why Nim generate C/C++ code?
    6. Is Nim a Transpiler?
    7. Why are unsigned types discouraged?
    8. Why doesn't default import statement force fully qualify access to the imported symbols?
  2. Coding
    1. Can I write expressions like x == a or x == b shorter?
    2. Can I write expressions like a <= x and x <= b shorter?
    3. How to make nested tables?
    4. How to get Nim version?
    5. How to skip writing import
    6. How to echo (or stringify) ptr or ref types?
    7. sizeof(ref object/seq/string) returns incorrect size
    8. I got compile error after adding or removing a space
    9. Can I use tab instead of space?
    10. Can I use curly brace instead of indentation?
    11. What is the difference between statement and expression?
    12. How to get each unicode characters from a string?
    13. How to leave nested for/while loops?
    14. How to iterate over every field of object or tuple types?
    15. How to modify each item inside for loop?
    16. Can I define operators for my type?
    17. Can I define custom pragmas?
    18. Can i pass GC'd memory across DLL boundaries?
    19. What is the difference between stack and heap memory?
    20. How to use hot code reloading?
    21. Can Nim do pointer arithmetic?
    22. How to safely convert int to enum?
    23. Can I use unicode for variable or procedure name?
    24. What is the difference between variables inside procedures and outside procedures?
  3. Type
    1. What is the difference between cint/cfloat and int/float?
    2. How to define recursive object type?
    3. How to get generic parameter from generic type?
    4. When to use 'ref object' vs plain 'object'?
    5. Can I pass/return object types to/from procedures if copying is disabled?
    6. Can I use object types for seq even if copying is disabled?
    7. How to store different types in seq?
    8. How pure pragma {.pure.} work to object type?
    9. Why proc type doesn't match even if proc signature is same?
  4. Procedures
    1. How to pass iterator to procedure?
    2. Can Nim create class method?
    3. How to get a pointer to a overloaded procedure?
    4. How to get a pointer to a generic procedure or a procedure with type class parameters?
    5. How to pass a specific overload to a macro?
    6. How to define constructors?
    7. How to use destructor?
    8. How to define a procedure that takes types?
    9. How to pass seq or array to varargs parameter?
    10. What is the difference between procedure, function and method?
    11. How to vary a return type of procedure at runtime?
    12. What 'GC safe' means?
    13. What is a calling convention?
    14. Why assigning procedures to variables causes compile error?
    15. Method call syntax with generics parameter causes compile error
  5. Compile Time
    1. How to run code at compile time?
    2. Is there restrictions on Compile-Time Execution?
    3. How to define a procedure that takes only constant expressions?
    4. Can I read a file at compile time?
    5. Can I execute an external command at compile time?
  6. Template
    1. What is a template?
    2. What is an untyped parameter type?
    3. What is a typed parameter type?
    4. Calling a template with method call syntax cause compile error
    5. I cannot use variables/types declared in template
    6. How to pass multiple code blocks to a template?
  7. Tools
    1. IDE or editor support for Nim?
    2. How to setup github action for nim?
    3. Is there a way to use Nim interactively? REPL for Nim?
  8. Libraries
    1. Is there list of Libraries or packages for Nim?
    2. Is there GUI libraries for Nim?
    3. Plotting library?
    4. Can I embed NimScript to my program?
    5. More Nim on more Microcontrollers!? (Arm CMSIS / Zephyr RTOS)
    6. Is there a list of programming language libraries for Nim or implementations written by Nim?
  9. Use Nim with other language
    1. How to use C/C++ libraries in Nim?
    2. How to use Python with Nim?
    3. How to call Rust code from Nim?
    4. How to use Windows .NET Frameworks from Nim?
    5. How to wrap const char* type in C?
    6. Nim bindings for the C++ STL?
  10. Nim Compiler
    1. Can I use Nim on Android?
    2. How Nim compiler process configuration files?
    3. How to make Android app?
    4. How to compile Nim to asmjs or wasm?
    5. Can I get precompiled latest devel Nim?
    6. How to produce assembler code?
    7. How to stop showing console window when my program starts?
    8. Is Nim/nimble virus or malware?
  11. Optimization
    1. How to write faster code?
    2. Profiler for Nim?
    3. Which compiler option generate fastest executable?
    4. Which compiler option generate smallest executable?
    5. My code is slower than python
    6. Arguments are copied when it is passed to a procedure?
    7. How procedure return value?
    8. Nim cannot be as fast as C or Rust because Nim uses garbage collector?
    9. If there are unused procedures, they makes executable file bigger?
  12. Macro
    1. What is macro?
    2. Is there any tutorial or documents to learn about macro?
    3. How to pass a int or string type as is?
    4. How to write a code that can be shared by multiple macros?
    5. Can I see what Nim code a macro generate?
    6. How to echo NimNode?
    7. How to pass multiple code blocks to a macro?
  13. Community
    1. Where can I ask a question about Nim?
    2. Where should I report security issue?
    3. How to donate to Nim?
    4. Is there Organization using Nim?
    5. Is there Game created with Nim?
    6. Where is Nim Community Survey Results?
    7. How to post Nim code with syntax highlight on Discord?
    8. How to use NimBot on Nim IRC/Discord?
    9. Are bots in Nim Discord channel AI?

Language design

What is Zen of Nim?

Why is Nim style insensitive?

People don't use Nim says case insensitivity cause confusion but I have never heard Nim's style insensitivity cause any problems from people use Nim.

Why Nim use space for indent instead of tab?

Why Nim use garbage collecter?

It makes managing heap memory easy.

Why Nim generate C/C++ code?

Is Nim a Transpiler?

No. If you say Nim is a transpiler, is GCC a transpiler from C to assembler?

Why are unsigned types discouraged?

Unsigned int types are used for algorithms that randomize bit patterns. For example:

Also used for protocols, formats or C libraries that requires unsigned int. For example, many image file formats or pixel buffer format uses 8 bit unsigned ints for RGB color value.

Why doesn't default import statement force fully qualify access to the imported symbols?

Coding

Can I write expressions like x == a or x == b shorter?

You can write it as x in [a, b].

If type of x is int8, int16, uint8, uint16, char or enum, you can write it using set type:

For example:

let x = 'a'
doAssert x in {'a'..'z', '0'..'9'}

type
  Direction = enum
    north, east, south, west

let d = Direction.east
doAssert d in {east, west}

Can I write expressions like a <= x and x <= b shorter?

You can write it as x in a..b.

How to make nested tables?

How to get Nim version?

On console, run nim -v.

Example output:

$ nim -v
Nim Compiler Version 2.0.2 [Linux: amd64]
Compiled at 2023-12-15
Copyright (c) 2006-2023 by Andreas Rumpf

git hash: c4c44d10df8a14204a75c34e499def200589cb7c
active boot switches: -d:release

In Nim code, you can use NimVersion const string or NimMajor, NimMinor and NimPatch const int in system module.

echo NimVersion
echo (NimMajor, NimMinor, NimPatch)

How to skip writing import

How to echo (or stringify) ptr or ref types?

Use repr.

Example code:

type
  Foo = ref object
    x: int

let a = Foo(x: 123)
echo a.repr

let ary = [Foo(x: 111), Foo(x: 222)]
echo ary.repr

var x = [0, 100, 300]
let ptrTox = x[1].addr
echo ptrTox.repr

Example output:

ref 0x7f09112ea050 --> [x = 123]
[ref 0x7f09112ea070 --> [x = 111], ref 0x7f09112ea090 --> [x = 222]]
ptr 0x55eda6b61108 --> 100

You can also dereference it if it is not nil and referencing valid memory location.

type
  Foo = ref object
    x: int

var a = Foo(x: 123)
echo a[]

var x = [0, 100, 300]
let ptrTox = x[1].addr
echo ptrTox[]

Example output:

(x: 123)
100

Define $ proc for ref type. $ proc converts given argument to string. $ is implicitly called when each arguments of echo are stringified.

type
  Foo = ref object
    x: int

proc `$`(foo: Foo): string =
  "Foo: " & $foo.x

let a = Foo(x: 123)
echo a

Example output:

Foo: 123

Or use https://github.com/treeform/pretty

sizeof(ref object/seq/string) returns incorrect size

ref object, seq and string are actually pointer to the heap memory that contains the content. So using sizeof to these type returns size of pointer. You can get size of the content using len proc to seq/string.

import std/sugar

type
  Foo = ref object
    x: array[123, int]

dump sizeof(Foo)
dump sizeof(Foo()[])
let f = new Foo
dump sizeof(f)
dump sizeof(f[])

dump sizeof(@[1, 2, 3])
let s = @[1, 2, 3]
dump sizeof(s[0]) * s.len

dump sizeof("foo")
let str = "foo"
dump sizeof(str)
dump str.len

Output:

sizeof(Foo) = 8
sizeof(Foo()[]) = 984
sizeof(f) = 8
sizeof(f[]) = 984
sizeof(@[1, 2, 3]) = 8
sizeof(s[0]) * s.len = 24
sizeof("foo") = 8
sizeof(str) = 8
str.len = 3

It is just an example output. Actual size of these types can be different when ARC/ORC is used or on 32bit CPU.

I got compile error after adding or removing a space

Nim is a white-space-sensitive language.

For example:

# `+` operator is used as a binary operator
echo 1 + 1

# Removed the space between '+' and '1'.
# Then `+` operator is used as an unary operator.
# And this is compile error
echo 1 +1
proc foo(x, y: int) = echo x, ", ", y

# Call foo with 2 ints.
foo(1, 2)

# If you put a space between 'foo' and '(1, 2),
# it become calling foo with a tuple (1, 2) in command invocation syntax
# It results in compile error as there is no `foo` that takes a tuple.
foo (1, 2)

If you want to see how Nim parses your code, use dumpTree macro in macros module:

import std/macros

dumpTree:
  echo 1 + 1
  echo 1 +1

Output:

StmtList
  Command
    Ident "echo"
    Infix
      Ident "+"
      IntLit 1
      IntLit 1
  Command
    Ident "echo"
    Command
      IntLit 1
      Prefix
        Ident "+"
        IntLit 1

Can I use tab instead of space?

No.

Can I use curly brace instead of indentation?

What is the difference between statement and expression?

How to get each unicode characters from a string?

You can use runes iterator in unicode module. unicode module provides support to handle the Unicode UTF-8 encoding.

import std/unicode

for c in "○△□◇".runes:
  echo c

Output:




If the output didn't displayed correctly, save source code as UTF-8 encode. Configure your console to use UTF-8 encode and use a font that support UTF-8 characters.

How to leave nested for/while loops?

You can do it by using named block.

For example:

block myblock:
  for i in 0..3:
    for j in 0..3:
      if i + j == 4:
        break myblock
      else:
        echo i, ", ", j

Templates or macros like this can be used for simpler code: https://github.com/demotomohiro/littlesugar

template namedWhile(name, cond, body: untyped): untyped =
  block name:
    while cond:
      body

var x = 5
namedWhile(myblock, x > 0):
  for i in 0..2:
    if x == 4 and i == 1:
      break myblock
    echo x, ", ", i
  
  dec x

How to iterate over every field of object or tuple types?

fieldPairs and fields iterators in iterators standard module can do: https://nim-lang.org/docs/iterators.html

For example:

type
  MyObj = object
    name: string
    num: int
    value: float

var x = MyObj(name: "foo", num: 2, value: 3.1)

for i in x.fields:
  echo i

How to modify each item inside for loop?

When for loop is used without explicitly specifying the iterator, item or pairs iterator is implicitly invoked. These iterators forbid modifying items.

mitems and mpairs iterators in iterators standard module allow modifying items: https://nim-lang.org/docs/iterators.html

For example:

var myarray = [1, 2, 3]

for i in myarray.mitems:
  i += 2

echo myarray

Can I define operators for my type?

You can define procedures that can be used like operators.

For example:

type
  MyVector = object
    data: array[3, float]

# You can call this proc like x + y
proc `+`(x, y: MyVector): MyVector =
  for i in 0..2:
    result.data[i] = x.data[i] + y.data[i]

let
  x = MyVector(data: [1.0, 2, 3])
  y = MyVector(data: [4.0, 2, 0])

echo x + y

proc `-`(x: MyVector): MyVector =
  for i in 0..2:
    result.data[i] = -x.data[i]

echo -x

# Customize how MyVector is stringified.
proc `$`(x: MyVector): string =
  result = "MyVector("
  for i in 0..2:
    result.add $x.data[i]
    if i < 2:
      result.add ", "
  result.add ")"

echo x

type
  MyMatrix = object
    data: array[4, float]

# You can read an element in a matrix like mat[i, j]
proc `[]`(x: MyMatrix; i, j: int): float =
  x.data[i + j * 2]

# You can set an element in a matrix like mat[i, j] = x
proc `[]=`(x: var MyMatrix; i, j: int; v: float) =
  x.data[i + j * 2] = v

var mat = MyMatrix(data: [0.0, 1, 2, 3])

echo mat[0, 1]
mat[1, 1] = -1
echo mat

See also:

Can I define custom pragmas?

There are 3 ways to create user defined pragmas:

Can i pass GC'd memory across DLL boundaries?

What is the difference between stack and heap memory?

How to use hot code reloading?

Can Nim do pointer arithmetic?

For example:

var testArray = [11.cint, 22, 33]
proc cfunc(): ptr cint = testArray[0].addr

var p = cast[ptr UncheckedArray[cint]](cfunc())
doAssert p[0] == 11
doAssert p[1] == 22
p[2] += 11
doAssert p[2] == 44

How to safely convert int to enum?

https://forum.nim-lang.org/t/8188

For non-holey enum types:

# Thank you PMunch!

type MyEnum = enum
  A
  B
  C

proc toEnum*[T](x: int): T =
  if x in T.low.int..T.high.int:
    T(x)
  else:
    raise newException(ValueError, "Value not convertible to enum")

var a = 2
echo toEnum[MyEnum](a)

var b = 100
# Error: unhandled exception: Value not convertible to enum [ValueError]
echo toEnum[MyEnum](b)

For enum types that can be non-holey or holey:

# Thank you Elegantbeef!

import std/macros

type
  MyEnum = enum
    A
    B
    C
  
  HoleyEnum = enum
    AVal = 3
    BVal = 5
    CVal = 9

macro enumElementsAsSet(enm: typed): untyped =
  newNimNode(nnkCurly).add(enm.getType[1][1..^1])

proc toEnum*(val: SomeInteger, E: typedesc[enum]): E =
  const enmRange = E.low.ord .. E.high.ord
  when E is Ordinal:
    if val in enmRange:
      E(val)
    else:
      raise (ref ValueError)(msg: $val & " cannot be converted to the enum: " & $E)
  else:
    if val in enmRange and val.E in E.enumElementsAsSet:
      E(val)
    else:
      raise (ref ValueError)(msg: $val & " cannot be converted to the enum: " & $E)

var a = 5

# Error: unhandled exception: 5 cannot be converted to the enum: MyEnum [ValueError]
let b = a.toEnum(MyEnum)

# Error: unhandled exception: 4 cannot be converted to the enum: HoleyEnum [ValueError]
let c = 4.toEnum(HoleyEnum)

Can I use unicode for variable or procedure name?

Yes you can.

const π  = 3.141

proc area□(w, h: float): float = w * h
proc area○(radius: float): float = π  * radius * radius

echo area□(3.0, 5.0)
echo area○(2.0)

You can use specific unicodes for operators: https://nim-lang.org/docs/manual.html#lexical-analysis-unicode-operators

func `±`[T](x: T): (T, T) = (x, -x)
doAssert ±3 == (3, -3)

func ``[T](x, y: set[T]): set[T] = x + y
doAssert ({1, 2, 3}  {2, 3, 4}) == {1, 2, 3, 4}

https://forum.nim-lang.org/t/10353

What is the difference between variables inside procedures and outside procedures?

Variables declared outside procedures are global variables and declared inside procedures are local variables.

# Global variable
var globalVar = 123

proc bar() =
  # Local variable
  var localVar = 456

You can add an export maker '*' to a global variable to make it accessible in other modules, but you cannot add it to local variables.

var globalVar* = 123

proc bar() =
  # Compile error: 'export' is only allowed at top level
  var localVar* = 456

Global variables lives while the program is running. Local variables are valid until exiting enclosing block or procedure. That means returning an address of global variable in a procedure is safe but returning an address of local variable is unsafe.

type
  SomeObj = object
    name: string

proc `=destroy`(x: SomeObj) =
  echo "Destroy ", x.name

var globalVar = SomeObj(name: "globalVar")

proc bar() =
  block:
    var localVarInBlock = SomeObj(name: "localVarInBlock")
    echo localVarInBlock
  
  var localVar = SomeObj(name: "localVar")
  echo localVar

echo globalVar
bar()

echo "End of program"

Output:

(name: "globalVar")
(name: "localVarInBlock")
Destroy localVarInBlock
(name: "localVar")
Destroy localVar
End of program
Destroy globalVar

Global variables are stored in data segment (translated to C global variables when compiled with C backend) and local variables are stored in stack. So declaring a large global variable works as long as there are enough memory but a large local variable can cause segmentation fault.

import std/os

var globalArray: array[10_000_000, int]

proc bar() =
  # Segmentatioon fault
  var localArray: array[10_000_000, int]
  echo "localArray: ", localArray[paramCount()]

echo "globalArray: ", globalArray[paramCount()]
bar()

When multiple threads are created, global variables without threadvar pragma are not created for each threads and shared by threads. So you need to use a lock to access global variables because reading and writing to the same memory location from multiple threads at the same time is undefined behavier unless it is an atomic variable. Local variables are created for each threads and not shared. So local variables in one thread are not read or written by other thread (as long as you don't share an address of local variabe to other thread) and you don't need to use a lock to access them.

import std/locks

var
  globalVar = 1
  thread1, thread2: Thread[int]
  lock: Lock

proc threadProc(threadID: int) {.thread.} =
  var localVar: int
  withLock(lock):
    localVar = globalVar
    echo "ThreadID: ", threadID
    echo "globalVar: ", globalVar
    echo "localVar: ", localVar
    echo "addr localVar: ", cast[uint](addr localVar)
    echo "addr globalVar: ", cast[uint](addr globalVar)
    inc globalVar

initLock(lock)
createThread(thread1, threadProc, 1)
createThread(thread2, threadProc, 2)
joinThread(thread1)
joinThread(thread2)
deinitLock(lock)

Output:

ThreadID: 2
globalVar: 1
localVar: 1
addr localVar: 140647661780424
addr globalVar: 94229087072600
ThreadID: 1
globalVar: 2
localVar: 2
addr localVar: 140647663893960
addr globalVar: 94229087072600

If the global pragma is applied to a local variable, it works like a global variable but you cannot access it outside the enclosing procedure. It is initialized once at program startup and holds the value after exiting the procedure.

import os

proc foo(x: int) =
  var g {.global.} = 0
  g += x
  echo g

foo(1)
foo(2)
foo(3)

# Compile Error: undeclared identifier: 'g'
#echo g

Output:

1
3
6

Type

What is the difference between cint/cfloat and int/float?

Types with 'c' prefix are corresponding to types in C language. cint is same to int in C and cfloat is same to float in C. They are used when you use C functions. sizeof(cint/cfloat) can be different from sizeof(int/float). Size of C types vary depending on the OS, CPU or backend C compiler. cint can be 32bits even on 64bit CPU (See 64-bit data models).

Bitwidth of int in Nim is always the same as a pointer (https://nim-lang.org/docs/system.html#int).

float in Nim is always 64bit (https://nim-lang.org/docs/manual.html#types-preminusdefined-floatingminuspoint-types).

How to define recursive object type?

You cannot define recursive object type in following way because object type is a value type and recursive object type needs infinite amount of memory:

type
  Foo = object
    child: Foo
  
  BarX = object
    y: BarY
  
  BarY = object
    x: BarX

You can use ref object type in following way:

type
  Foo = ref object
    child: Foo
  
  BarX = ref object
    y: BarY
  
  BarY = ref object
    x: BarX

var foo = Foo(child: Foo())
echo foo[].repr

var bar = BarX(y: BarY(x: BarX()))
echo bar[].repr

Use seq or other collection types that store values in heap:

type
  Foo = object
    child: seq[Foo]

let foo = Foo(child: @[Foo(), Foo()])
echo foo

import std/tables

type
  Bar = object
    table: Table[string, Bar]

let bar = Bar(table: toTable {"abc": Bar(), "xyz": Bar()})
echo bar

How to get generic parameter from generic type?

type
  Foo[T] = object
    x: T
  
  Bar = Foo[char]

let foo = Foo[float](x: 12.3)
echo foo.T
echo Bar.T

When to use 'ref object' vs plain 'object'?

Reference types (ref object) refer an object on heap. Mutiple references can refer an object. Reference types are actually pointers and Nim manage referenced objects so that you can use them safely.

type
  MyObj = object
    x: int
  
  MyRefObj = ref MyObj

# Variables declared outside of procedures are on static storage.
# Static storage lives while the program is running and its size is fixed.
# Variables on static storage can be accessed by any procedures in same module.
# And if they have export mark, can be accessed by any procedures in other modules importing it.

let
  myObj = MyObj() # Exists on static storage
  myRefObj = MyRefObj() # myRefObj exists on static storage and points to MyObj type object created on heap

proc myProc() =
  # Variables declared inside of procedures are on stack.
  # Stacks are created when a procedure is called and freed when returning from it.
  
  var
    myObjInProc = MyObj() # Exists on stack
    myRefObjInProc = MyRefObj() # myRefObjInProc exists on stack and points to MyObj type object created on heap
  
  let
    myObjInProc2 = myObjInProc  # myObjInProc2 exists on stack and myObjInProc is copied to myObjInProc2
    myRefObjInProc2 = myRefObjInProc # myRefObjInProc2 exists on stack and myRefObjInProc and myRefObjInProc2 points to same object
  
  myObjInProc.x = 123
  
  doAssert myObjInProc.x == 123
  doAssert myObjInProc2.x == 0
  
  myRefObjInProc.x = 321
  
  doAssert myRefObjInProc.x == 321
  doAssert myRefObjInProc2.x == 321
  
  myRefObjInProc2[] = myObjInProc # `[]` operator dereference a reference type
                                  # myObjInProc is copied to the object myRefObjInProc2 points
  
  doAssert myRefObjInProc.x == 123
  doAssert myRefObjInProc2.x == 123
  
  var
    myObjInSeq = newSeq[MyObj](4) # 4 MyObj exist on heap contiguously
    myRefObjInSeq = newSeq[MyRefObj](4) # 4 MyRefObj exist on heap contiguously and they are nil
    myRefObjInSeq2 = @[MyRefObj(x: 1), MyRefObj(x: 2), MyRefObj(x: 3), MyRefObj(x: 4)]  # each 4 MyRefObj points to 4 MyObj on heap respectively.
                                                                                        # These 4 MyObj might not placed on heap contiguously.
  
  myObjInSeq[0] = myObjInProc # myObjInProc is copied to myObjInSeq[0]
  
  myRefObjInSeq[0] = myRefObjInProc
  myRefObjInSeq[1] = myRefObjInProc2
  # myRefObjInSeq[0], myRefObjInSeq[1], myRefObjInProc and myRefObjInProc2 refers same MyObj
  
  myRefObjInSeq[2] = MyRefObj() # New MyObj is created on heap and myRefObjInSeq[2] refers it
  
  # The stack allocated for all variables in this proc is freed.
  # So all objects referenced by ref types in this scope are freed because there is no reference refers them.

myProc()

type
  SomeObj = object
    x: int
    o1: MyObj
    o2: MyObj   # x, o1, o2, o3 are placed on the memory contiguously
    o3: MyRefObj
  
  SomeRefObj = ref SomeObj

proc myNextProc =
  var
    s = SomeObj(o3: MyRefObj()) # s.o3 refers MyObj on heap
    s2 = SomeRefObj(o3: s.o3) # SomeObj is created on heap. That means all fields of s2 (x, o1, o2, o3) are on heap.

myNextProc()

type
  DontCopyMe = object
    x: int

# This proc makes copying `DontCopyMe` compile error.
proc `=copy`(dest: var DontCopyMe; src: DontCopyMe) {.error.}

var
  x, y: DontCopyMe

# This code generates compile error
# y = x

echo x, y

type
  BaseObj = object of RootObj
    x: int
  
  BaseRefObj = ref BaseObj
  
  InheritObj = object of BaseObj
    y: int
  InheritObj2 = object of BaseObj
  
  InheritRefObj = ref InheritObj
  InheritRefObj2 = ref InheritObj2

proc testInheritance =
  var
    a = InheritObj(x: 7)
    b: BaseObj = a    # Copies only BaseObj part of InheritObj to b
  echo b
  # echo InheritObj(b)  # Invalid object conversion
  
  var
    inheritRef = InheritRefObj(y: 1234)
    baseRef: BaseRefObj = inheritRef  # baseRef points to InheritObj
  
  doAssert baseRef of InheritRefObj
  doAssert not (baseRef of InheritRefObj2)
  doAssert InheritRefObj(baseRef).y == 1234

testInheritance()

Can I pass/return object types to/from procedures if copying is disabled?

Procedures can take or return uncopyable objects but there are restrictions. There are cases you need to add sink to parameters or lent to return type.

type
  DontCopyMe = object
    x: int

# Make copying `DontCopyMe` compile error
proc `=copy`(dest: var DontCopyMe; src: DontCopyMe) {.error.}

# See when move happen to `DontCopyMe`
proc `=sink`(dest: var DontCopyMe; src: DontCopyMe) =
  echo "Sink! ", src.x
  dest.x = src.x

func getX(x: DontCopyMe): int = x.x

proc init(T: typedesc[DontCopyMe]; x: int): DontCopyMe =
  DontCopyMe(x: x)

# func retAsIs(x: DontCopyMe): DontCopyMe = x
# Error: '=copy' is not available for type <DontCopyMe>

# proc takeVarAndRet(x: var DontCopyMe): DontCopyMe = x
# Error: '=copy' is not available for type <DontCopyMe>

proc takeVarAndRetVar(x: var DontCopyMe): var DontCopyMe = x

proc sinkAndRet(x: sink DontCopyMe): DontCopyMe = x

proc retLent(x: DontCopyMe): lent DontCopyMe = x

proc test =
  let a = DontCopyMe(x: 1)
  echo getX(a)
  
  var b = DontCopyMe.init(10)
  echo b
  
  # echo retAsIs(a)
  
  var c = DontCopyMe(x: 2)
  # echo takeVarAndRet(c)
  echo takeVarAndRetVar(c)
  let d = sinkAndRet(c)
  echo d
  
  var e = retLent(DontCopyMe(x: 3))
  echo e
  # let f = retLent(e)  # Error: '=copy' is not available for type <DontCopyMe>;

test()

type
  HaveDontCopyMe = object
    dont: DontCopyMe

#[
proc init(T: typedesc[HaveDontCopyMe]; d: DontCopyMe): HaveDontCopyMe =
  HaveDontCopyMe(dont: d)
]#
# Error: '=copy' is not available for type <DontCopyMe>

proc init(T: typedesc[HaveDontCopyMe]; d: sink DontCopyMe): HaveDontCopyMe =
  HaveDontCopyMe(dont: d)

# func getDontCopyMe(x: HaveDontCopyMe): DontCopyMe = x.dont
# Error: '=copy' is not available for type <DontCopyMe>;

func getDontCopyMeLent(x: HaveDontCopyMe): lent DontCopyMe = x.dont
func getDontCopyMeVar(x: var HaveDontCopyMe): var DontCopyMe = x.dont

# proc setDontCopyMe(x: var HaveDontCopyMe; y: DontCopyMe) = x.dont = y
# Error: '=copy' is not available for type <DontCopyMe>;

func setDontCopyMeSink(x: var HaveDontCopyMe; y: sink DontCopyMe) = x.dont = y

proc test2 =
  let a = HaveDontCopyMe.init(DontCopyMe(x: 100))
  echo a
  
  # let b = a.getDontCopyMe()
  # echo a.getDontCopyMe()
  # let c = a.getDontCopyMeLent()
  # echo c
  
  echo a.getDontCopyMeLent()
  
  var d = HaveDontCopyMe.init(DontCopyMe(x: 110))
  d.getDontCopyMeVar().x = 111
  echo d
  
  # var e = HaveDontCopyMe.init(DontCopyMe(x: 120))
  # e.setDontCopyMe(DontCopyMe(x: 121))
  # echo e
  
  var f = HaveDontCopyMe.init(DontCopyMe(x: 130))
  f.setDontCopyMeSink(DontCopyMe(x: 131))
  echo f
  
  var ff = DontCopyMe(x: 142)
  f.setDontCopyMeSink(ff)
  echo f
  # echo ff # Reading `ff` cause compile error to `f.setDontCopyMeSink(ff)` in above line

test2()

Can I use object types for seq even if copying is disabled?

You can use uncopyable object types with seq but there are restrictions.

type
  DontCopyMe = object
    x: int

# Make copying DontCopyMe compile error
proc `=copy`(dest: var DontCopyMe; src: DontCopyMe) {.error.}
proc `=sink`(dest: var DontCopyMe; src: DontCopyMe) =
  echo "Sink! ", src.x
  dest.x = src.x

var s: seq[DontCopyMe]
s.add(DontCopyMe(x: 1))
s.add(
  block:
    var a = DontCopyMe(x: 2);
    a)
echo s

s.setLen(64)
echo s[^1]
s.insert(DontCopyMe(x: 3), 60)
echo s

block:
  var
    sa = @[DontCopyMe(x: 4), DontCopyMe(x: 5)]
    sb = @[DontCopyMe(x: 6), DontCopyMe(x: 7)]
  
  # echo sa & sb # Error: '=copy' is not available for type <seq[DontCopyMe]>

block:
  var a = DontCopyMe(x: 3)
  # s.add(a) # Compile error: '=copy' is not available for type <DontCopyMe>; requires a copy because it's not the last read of 'a'; routine: testobj
  # Variables in outside of procedures cannot be moved?

proc test =
  block:
    var
      sl: seq[DontCopyMe]
      a = DontCopyMe(x: 11)
    
    sl.add(a)
    
    echo sl
    # echo a  # Reading `a` makes `sl.add(a)` in above line to compile error
  
  block:
    var
      sl = @[DontCopyMe(x: 21)]
      a = sl[0]
    
    echo a
    # echo sl # Reading `sl` makes `a = sl[0]` in above line to compile error
    # sl.add DontCopyMe(x: 22)  # Adding new element also makes `a = sl[0]` to compile error
    
    sl = @[DontCopyMe(x: 23), DontCopyMe(x: 24)]
    
    var
      b = sl[0]
      c = sl[1]
    echo b, c
    
    sl = @[DontCopyMe(x: 25), DontCopyMe(x: 26)]
    
    var
      idx = (cast[int](addr sl[0]) shr 5) and 1 # Get a runtime value that compiler cannot see
      d = sl[idx]
      #e = sl[idx xor 1] # Error: '=copy' is not available for type <DontCopyMe>
    
    echo d
  
  block:
    var
      sl = @[DontCopyMe(x: 31), DontCopyMe(x: 32)]
      a = sl.pop
    
    doAssert sl.len == 1
    doAssert a == DontCopyMe(x: 32)
    echo sl
    echo a
  
  block:
    var
      sa = @[DontCopyMe(x: 44), DontCopyMe(x: 45)]
      sb = @[DontCopyMe(x: 46), DontCopyMe(x: 47)]
      sab = sa & sb
    echo sab
    # echo sa # Reading `sa` makes `sab = sa & sb` in above line to compile error

test()

How to store different types in seq?

You cannot store different types in seq. Each elements in seq are placed in memory continuously and you can random access each elements in O(1) because it stores only 1 type.

Workarounds:

# Thank you Elegantbeef!

type
  BoxBase = ref object of RootObj
  Boxed[T] = ref object of BoxBase
    data: T

proc boxed[T](a: T): Boxed[T] = Boxed[T](data: a)

var a = @[BoxBase boxed"hello", boxed(10), boxed(30'd)]
for x in a:
  if x of Boxed[int]:
    echo "int ", Boxed[int](x).data
  elif x of Boxed[float]:
    echo "float ", Boxed[float](x).data
  elif x of Boxed[string]:
    echo "string ", Boxed[string](x).data

How pure pragma {.pure.} work to object type?

Object types can inherit from existing object if it is RootObj, inherits from RootObj or has inheritable pragma. Objects with inheritance enabled has hidden runtime type information so that you can use of operator to determine the object's type. In other words, objects without inheritance don't have runtime type information.

pure pragma works to enum type differently: https://nim-lang.org/docs/manual.html#types-enumeration-types

pure pragma remove the runtime type information even if an object can be inherit from. Then you cannot use of operator to such an object.

type
  PureBase {.pure, inheritable.} = object
    x: int64
  
  NonPureBase = object of RootObj
    x: int64
  
  FromPure = object of PureBase
    y: int64
  
  FromNonPure = object of NonPureBase
    y: int64
  
  NotInheriable = object
    x: int64
    y: int64

proc test(a: PureBase) =
  # Error: no 'of' operator available for pure objects
  if a of FromPure:
    echo "FromPure"

proc test(a: NonPureBase) =
  if a of FromNonPure:
    echo "FromNonPure"

var
  pureObj = FromPure()
  nonPureObj = FromNonPure()

echo sizeof(NotInheriable)  # 16
echo sizeof(pureObj)        # 16
echo sizeof(nonPureObj)     # 24

test(pureObj)
test(nonPureObj)

pure pragma should be used with inheritable pragma. It doesn't works when an object inherits from RootObj.

type
  PureRoot {.pure.} = object of RootObj
    x: int64
  
  FromPureRoot = object of PureRoot
    y: int64

proc test(a: PureRoot) =
  # You can use of operator
  if a of FromPureRoot:
    echo "FromPureRoot"

var fromPureRoot = FromPureRoot()

echo sizeof(fromPureRoot) # 24
test(fromPureRoot)

Why proc type doesn't match even if proc signature is same?

Even if proc signature is the same, proc types doesn't match if the calling conventions were different: https://nim-lang.org/docs/manual.html#types-procedural-type

For example:

proc foo(x: int): int =
  x + x

proc bar(x: int): int {.cdecl.} =
  x * x

proc calls(x: int; callback: proc(x: int): int {.nimcall.}): int =
  callback(x * 2)

echo calls(3, foo)
# Error: type mismatch
echo calls(4, bar)

proc calls2(x: int; callback: proc(x: int): int {.closure.}): int =
  callback(x + 1)

echo calls2(3, foo)
# Error: type mismatch
echo calls2(4, bar)

See also: Why assigning procedures to variables causes compile error?

Procedures

How to pass iterator to procedure?

Can Nim create class method?

Nim language doesn't have class method. But you can define a procedure similar to class method.

type
  Foo = object
    x: int

proc myClassMethod(f: typedesc[Foo]; param: int): Foo =
  Foo(x: param)

let foo = myClassMethod(Foo, 123)
echo foo

How to get a pointer to a overloaded procedure?

Type conversion can be used to disambiguate overloaded routines.

proc foo(x: int) = echo x
proc foo(x: string) = echo x

# Compile error
#let pfoo = foo

let pfoo1: proc (x: int) = foo
pfoo1(1)

let pfoo2 = (proc (x: int))foo
pfoo2(2)

How to get a pointer to a generic procedure or a procedure with type class parameters?

Specify all generic parameters or use type conversion.

proc foo[T](x: T) = echo x

# Compile error
#let pfoo = foo

let pfoo1 = foo[int]
pfoo1(1)

let pfoo2: proc (x: int) = foo
pfoo2(2)

let pfoo3 = (proc (x: int))foo
pfoo3(3)
proc foo(x: int or string) = echo x

# Compile error
#let pfoo = foo

let pfoo1: proc (x: int) = foo
pfoo1(1)

let pfoo2 = (proc (x: int))foo
pfoo2(2)

proc bar[T](x: int or string; y: T) = echo x, y

let pbar1: proc (x: int; y: string) = bar
pbar1(1, "bar")

let pbar2 = (proc (x: int; y: string))bar
pbar2(2, "bar")

How to pass a specific overload to a macro?

How to define constructors?

In Nim, everything is initialized as all bits zero in default.

See:

You can assign a constant default value to an object field. But you cannot use a runtime value as a default value:

If you want to initialize objects with arguments and initializing code, people usually create createFoo or initFoo procedure.

Example code:

type
  Foo = object
    names: seq[string]

proc initFoo(x: var Foo, name: string) =
  x.names.add name

var foo: Foo
foo.initFoo("abc")
echo foo

type
  Bar = ref object
    names: seq[string]

proc createBar(name: string): Bar =
  Bar(names: @[name])

var bar = createBar("xyz")
echo bar[]

See also:

How to use destructor?

How to define a procedure that takes types?

How to pass seq or array to varargs parameter?

Pass them as is:

proc foo(args: varargs[string]) =
  for s in args:
    echo s

let myarray = ["foo", "bar"]
foo(myarray)

let myseq = @["one", "two"]
foo(myseq)

Output:

foo
bar
one
two

What is the difference between procedure, function and method?

Function is a procedure with noSideEffect pragma.

How to vary a return type of procedure at runtime?

You cannot change return type because Nim is a statically typed programming language.

Workarounds:

What 'GC safe' means?

We call a procedure p GC safe when it doesn't access any global variable that contains GC'ed memory (string, seq, ref or a closure) either directly or indirectly through a call to a GC unsafe proc.

It is related to Nim's memory model for threads.

What is a calling convention?

Meaning of "Calling convention" in C/Assembler and Nim is bit different.

In C/Assembler, calling convention is about how parameters are passed to a function, result is returned and other low level rules for functions. https://en.wikipedia.org/wiki/Calling_convention

In C or Nim language, you see no differences between functions with different calling conventions excepts function signature has different calling convention name. But they are different in assembly or machine language level.

In some platforms, many calling conventions are used. When you call a function using a pointer to function, calling convention of the function pointer type and the calling convention of the function being called must be the same. If they are different, it results in compile error or undefined behavier. When you call a C function from Nim and pass a pointer to Nim procedure to the C function, calling convention of the Nim procedure must be the same calling convention to the C function pointer type of the parameter.

stdcall, cdecl, safecall, fastcall, thiscall, syscall are C/C++/Assembler calling conventions and they are explained here: https://en.wikipedia.org/wiki/X86_calling_conventions

In Nim, calling convention is about C/Assembler calling convention, how Nim generate C code from Nim procedure and higher level things. This section in Nim manual lists Nim calling conventions: https://nim-lang.org/docs/manual.html#types-procedural-type

Why assigning procedures to variables causes compile error?

Procedural type

A subtle issue with procedural types is that the calling convention of the procedure influences the type compatibility: procedural types are only compatible if they have the same calling convention. As a special extension, a procedure of the calling convention nimcall can be passed to a parameter that expects a proc of the calling convention closure.

Example code:

type
  # MyProcType is closure
  MyProcType = proc (x: int)

# Calling convention of myProc is `nimcall` and not closure.
proc myProc(x: int) =
  echo x

echo MyProcType is proc (x: int) {.closure.} # true
echo MyProcType is proc (x: int) {.nimcall.} # false
echo myProc is proc (x: int) {.closure.}     # false
echo myProc is proc (x: int) {.nimcall.}     # true

var a: MyProcType = myProc
a(123)
type MyProcType2 = proc (x: int) {.nimcall.}
var b: array[2, MyProcType2] = [myproc, myproc]
b[0](123)

# type mismatch: got 'array[0..1, proc (x: int){.gcsafe.}]' for '[myProc, myProc]' but expected 'array[0..1, MyProcType]'
# Calling convention mismatch: got '{.nimcall.}', but expected '{.closure.}'
#var c: array[2, MyProcType] = [myproc, myproc]
#c[0](321)
proc foo(x: int): int =
  echo x
  x + 1

# The type of myProc is the same to the type of foo.
# Because nimcall is the default calling convention used for a Nim proc,
# the calling convention of myProc is nimcall.
var myProc = foo

# if the calling convention of myProc was closure, there is no compile error.
# closure is the default calling convention for a procedural type that lacks any pragma annotations.
# You can assign a procedure of the calling convention nimcall to a procedual type variable of the calling convention closure.
#var myProc: proc (x: int): int = foo

echo myProc is proc (x: int): int {.nimcall.}   # true
echo myProc is proc (x: int): int               # false

let y = 3
# This compiles as this proc is not a closure.
myProc = proc (x: int): int =
  x + y
echo myProc(2)

proc getSomeProc(y: int): proc (x: int): int =
  let z = y * y
  # Only closure calling convention can capture local variables.
  proc (x: int): int =
    x + z

# Error: type mismatch
# Because getSomeProc return a proc with closure calling convention.
myProc = getSomeProc(100)
echo myProc(1)

Method call syntax with generics parameter causes compile error

proc foo[T](x: string) =
  echo T, x

"Test".foo[:int]

See: Method call syntax

Compile Time

Run your code in nim c myprogram.nim that completes before Nim output executable file or print error.

How to run code at compile time?

Expression in const statement is evaluated at compile time:

proc collatz(n: int): int =
  var x = n
  while x != 1:
    if x mod 2 == 0:
      x = x div 2
    else:
      x = 3 * x + 1
    inc result

# collatz(12) is executed at compile time.
const a = collatz(12)

# collatz(12) is executed at runtime time.
let b = collatz(12)
echo a
echo b

Use static statement/expression:

proc foo(x, y: int): int =
  when nimvm: x else: y

# Executed at runtime
let v = foo(1, 2)
echo v

# foo(1, 2) is executed at compile time
let w = static foo(1, 2)
echo w

# Following statement is executed at compile time
static:
  var x = foo(1, 2)
  x = x * 7
  echo "This message is displayed at compile time"
  echo x

Is there restrictions on Compile-Time Execution?

Yes, there is restrictions

How to define a procedure that takes only constant expressions?

Can I read a file at compile time?

Can I execute an external command at compile time?

Template

What is a template?

Templates in Nim can be called like procedures and it works as if it inserts code in the call site.

It is similar to macros in C language but safer and nicer.

For example:

template foo(x: string): untyped =
  echo "From template foo"
  echo x

echo "Calling template foo"
foo("test")

Output:

Calling template foo
From template foo
test

A difference between templates and procedures is templates can take a name and create a new variable/procedure with it in current scope or take code block. It can also takes inline iterators using iterable type class.

For example:

template defineProc(procName: untyped): untyped =
  proc procName() =
    echo "Procedure defined in template"

defineProc(myProcedure)
myProcedure()

template declareVar(varName: untyped): untyped =
  var varName {.inject.} = "Variable declared in template"

declareVar(myVariable)
echo myVariable

Output:

Procedure defined in template
Variable declared in template

Example code that takes code block:

import std/[times, os]

template measureTime(body: untyped): untyped =
  block:
    let begin = epochTime()
    body
    let delta = epochTime() - begin
    echo "Time: ", delta

measureTime:
  os.sleep(500)
  os.sleep(600)

Output :

Time: 1.100237131118774

Example code that takes inline iterator:

iterator myIter(n: openArray[int]): int =
  for i in n:
    yield i * i

template myTemplate(iter: iterable[int]): int =
  var sum = 0
  for i in iter:
    sum += i
  sum

echo myTemplate(myIter(@[-1, 0, 1]))
echo myTemplate(1..3)

Output:

2
6

See also:

What is an untyped parameter type?

You can pass undeclared identifiers to untyped parameters to declare new variables or procedures. Or you can pass expressions/statements/code blocks that contain undeclared identifiers and use variables or procedures inside the template.

See also:

What is a typed parameter type?

You can pass any expressions or statements to typed parameter as long as they are valid Nim code and don't use undeclared identifiers.

If your template take expressions or statements that use variables declared in the template, you need to use untyped parameter, not typed.

See also:

Calling a template with method call syntax cause compile error

You cannot use method call syntax when first parameter of a template/macro is untyped.

See also:

I cannot use variables/types declared in template

If you want to use variables or types declared in template outside of it, you need to use inject pragma. You can use procedures, iterators, converters, templates or macros defined in template outside of it without inject pragma.

For example:

template declVarAndType(body: untyped): untyped =
  type
    Foo {.inject.} = object
      x: int
  
  var myVar {.inject.} = "Variable in template"
  body

declVarAndType:
  let f = Foo()
  echo myVar

let g = Foo(x: 123)
echo myVar

See also:

How to pass multiple code blocks to a template?

You can pass multiple code blocks to a template or macro using do notation.

For example:

template foo(x: bool; bodyA, bodyB: untyped): untyped =
  if x:
    bodyA
  else:
    bodyB

foo(false) do:
  echo "bodyA"
do:
  echo "bodyB"

Tools

IDE or editor support for Nim?

How to setup github action for nim?

Is there a way to use Nim interactively? REPL for Nim?

Libraries

Is there list of Libraries or packages for Nim?

Is there GUI libraries for Nim?

Plotting library?

Can I embed NimScript to my program?

More Nim on more Microcontrollers!? (Arm CMSIS / Zephyr RTOS)

Is there a list of programming language libraries for Nim or implementations written by Nim?

Use Nim with other language

How to use C/C++ libraries in Nim?

How to use Python with Nim?

How to call Rust code from Nim?

How to use Windows .NET Frameworks from Nim?

How to wrap const char* type in C?

type
  cstringConstImpl {.importc:"const char*".} = cstring
  constChar* = distinct cstringConstImpl

{.emit: "const char* foo() {return \"hello\";}".}
proc foo(): constChar {.importc.} # change to importcpp for C++ backend
echo foo().cstring

Nim bindings for the C++ STL?

Nim Compiler

See also: Official Nim Compiler User Guide

Can I use Nim on Android?

You can use Nim compiler on android using Termux. You can install Nim on Termux with pkg install nim command.

How Nim compiler process configuration files?

*.cfg: https://nim-lang.org/docs/nimc.html#compiler-usage-configuration-files

*.nims: https://nim-lang.org/docs/nims.html

How to make Android app?

How to compile Nim to asmjs or wasm?

Can I get precompiled latest devel Nim?

How to produce assembler code?

There are 2 ways to produce assembler code:

How to stop showing console window when my program starts?

Compile your code with --app:gui option like:

$ nim c --app:gui myprog.nim

For example:

import winim

doAssert MessageBoxA(0, "Hello world", "Nim", MB_OK)

When you start your program on windows explorer, new console window will open if you compiled it without --app:gui option.

$ nim c test myprog.nim

If you compile it with --app:gui, your program runs without opening console window.

Is Nim/nimble virus or malware?

Nim and tools distributed with Nim are neither virus nor malwares. They are safe to use.

Many people reported Nim and executables compiled with Nim were detected by antivirus softwares but they are false positive.

It seems Nim was used to write malware and it might related to false detections by many Antivirus softwares:

And it seems antivirus softwares don't analyze the program carefully, but just alarm when they spot a bit pattern that has been seem in other malware.

Please do not write malware in Nim.

Optimization

How to write faster code?

Profiler for Nim?

Which compiler option generate fastest executable?

If you use gcc or clang backend compiler,

Example command:

$ nim c -d:danger -d:lto --passC:"-march=native" test.nim

Which compiler option generate smallest executable?

If you use gcc or clang backend compiler,

Example command:

$ nim c -d:danger --mm:arc -d:lto -d:strip --opt:size test.nim

See Nim Compiler User Guide for more details.

My code is slower than python

Pass -d:release or -d:danger option to Nim. Nim optimize code when they are specified. With -d:release runtime checks are enabled but -d:danger removes any runtime checks.

Arguments are copied when it is passed to a procedure?

How procedure return value?

Nim cannot be as fast as C or Rust because Nim uses garbage collector?

Nim can be as fast as C or Rust without turn off memory management. Using GC doesn't mean every objects are placed on heap. You can choose to place arrays or objects in stack and they are not managed by GC. You can control memory layout of data structure like C.

type
  Vec3 = object
    x, y, z: float32
  
  Triangle = object
    v: array[3, Vec3]

# float32 are placed on memory continuously
doAssert sizeof(array[16, Triangle]) == sizeof(float32) * 3 * 3 * 16

RefC (Default Garbage collector), ORC and ARC don't start garbage collection at random timing. If you don't allocate new heap in inner loop, garbage collection doesn't start in the loop.

These memory managements add a counter to each heap memory. Most of case, memory usage increase due to a counter is tiny because objects allocated in the heap is larger than a counter. As they don't use atomic instruction, cost of incrementing/decrementing counter is tiny unless you just copy reference types and don't do other thing. The Nim compiler also aggressively optimizes away RC ops and exploits move semantics.

If there are unused procedures, they makes executable file bigger?

Nim do dead code elimination.

Macro

What is macro?

Like templates work as if it inserts Nim code where it is called, macros work as if it inserts some code where it is called. Unlike template, you construct a data structure called "abstract syntax tree" (AST) in macro.

An AST is a tree data consists of nodes that is easy to handle for compiler and macro. When Nim compiles your code, it constructs AST from your source code. Some nodes have variable number of other nodes as children, and other nodes are leaf nodes don't have child. Reaf nodes correspond to number/string literals or name of variable, procedure, iterator, etc. Nodes also have kind field.

For example, expression x + 123 (x is int variable) becomes a node with Infix kind that has 3 children, Ident (identifier) kind node with name "+", Ident kind node with name "x" and IntLit (int literal) kind node with number "123".

Following code prints AST of x + 123 using dumpTree in macros module at compile time:

import macros

let x = 1
dumpTree(x + 123)

Output:

Infix
  Ident "+"
  Ident "x"
  IntLit 123

You can use dumpTree to prints AST of any Nim code:

import macros

dumpTree:
  let x = 1
  echo x + 123

Output:

StmtList
  LetSection
    IdentDefs
      Ident "x"
      Empty
      IntLit 1
  Command
    Ident "echo"
    Infix
      Ident "+"
      Ident "x"
      IntLit 123

NimNode type repesents a node in AST and NimNodeKind enum repesents a kind of node.

Macros can take expressions or statements and they become NimNode AST in macro. So you can access any child nodes inside given nodes. You can analyze given expressions or statements in macro. You can transform them to construct new AST or just put them inside newly created AST and return it. AST you constructed and returned in your macro is inserted in where your macro is called.

Writing a Nim macro is like a writing a program that program a program.

Is there any tutorial or documents to learn about macro?

How to pass a int or string type as is?

A macro with int type parameters takes any expression with int and it become NimNode in the macro:

import macros

macro foo(x: int): untyped =
  echo typeof(x)
  echo x.treeRepr

var
  x = 1
  y = 2
foo(x + y)

Output:

NimNode
Infix
  Sym "+"
  Sym "x"
  Sym "y"

If you want to write a macro that takes int value as is, use static[int]:

import macros

macro foo(x: static[int]): untyped =
  echo typeof(x)
  echo x

foo(123)

Output:

int
123

How to write a code that can be shared by multiple macros?

Macro can call procedures as long as it can be executed at compile time. You can implement procedures that can transform AST like you do in macros using NimNode parameters and return type.

import macros

proc addEcho(x: NimNode): NimNode =
  newCall(bindSym"echo", x)

macro fooEcho(x: string): untyped =
  x.addEcho

macro fooEcho(x: int): untyped =
  x.addEcho

fooEcho("test")
fooEcho(100 + 23)

Can I see what Nim code a macro generate?

How to echo NimNode?

import macros

macro foo(x: float): untyped =
  echo "treeRepr:"
  echo x.treeRepr
  echo ""
  echo "lispRepr:"
  echo x.lispRepr
  echo ""
  echo "astGenRepr:"
  echo x.astGenRepr
  echo ""
  echo "repr:"
  echo x.repr

let
  x = 1.0
  y = 10.0
foo(x + y)

Output:

treeRepr:
Infix
  Sym "+"
  Sym "x"
  Sym "y"

lispRepr:
(Infix (Sym "+") (Sym "x") (Sym "y"))

astGenRepr:
nnkInfix.newTree(
  newSymNode("+"),
  newSymNode("x"),
  newSymNode("y")
)

repr:
x + y

How to pass multiple code blocks to a macro?

You can pass multiple code blocks to a template or macro using do notation.

For example:

import macros

macro foo(bodyA, bodyB: untyped): untyped =
  echo bodyA.treeRepr
  echo bodyB.treeRepr

foo() do:
  echo "bodyA"
do:
  echo "bodyB"

Community

Where can I ask a question about Nim?

Chat:

Where should I report security issue?

How to donate to Nim?

Is there Organization using Nim?

Is there Game created with Nim?

Where is Nim Community Survey Results?

How to post Nim code with syntax highlight on Discord?

Enclose your code with ```nim and ``` like:

```nim
var
  x = 1
  y = 2

echo x + y
```

How to use NimBot on Nim IRC/Discord?

You can run one line Nim code with !eval echo "Hello".

Are bots in Nim Discord channel AI?

They are AIs written with Nim by AI researchers. They learn about Nim and how to talk like human. After enough amount of learning, they can ask or answer Nim questions like human.

It is joke. There is bridge between discord and other chat systems like IRC or matrix. When people in other chat system write message, discord shows it with user name and bot mark. But in the Internet, how can we know whether messages are really written by human without meeting face to face? What if someone or something answers your Nim question is extraterrestrial intelligence or genetically engineered highly intelligent dog? Is there any problems even if they talk about Nim politely?


by Tomohiro

index