16
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[Nim] Basics

Last updated at Posted at 2015-07-18

Definitions

var
  a = "a"  # l-value

const
  b = "a"  # not l-value

let
  c = "a" # not l-value

var
  p: ptr string

echo(repr(a))
# --> 0x7fd3b9d09050"a"

p = addr(a)

echo(repr(p))
# --> ref 0x622958 --> 0x7fd3b9d09050"a"

#p = addr(b)
# static error: expression has no address

#p = addr(b)
# static error: expression has no address

Lexical Analysis

Encoding

All Nim source files are in the UTF-8 encoding (or its ASCII subset). Other encodings are not supported.

Indentation

Comments

var
  x = 1  # comment piece
  y = 1  # [comment start] comment piece
# comment piece
# comment piece [comment end]

## Documentation comment
## Documentation comment

Identifiers & Keywords

letter ::= 'A'..'Z' | 'a'..'z' | '\x80'..'\xff'
digit ::= '0'..'9'
IDENTIFIER ::= letter ( ['_'] (letter | digit) )*

Two immediate following underscores __ are not allowed

Identifier equality

a[0] == b[0] and a.replace("_", "").toLower == b.replace("_", "").toLower

String literals

Triple quoted string literals

When the opening """ is followed by a newline (there may be whitespace between the opening """ and the newline), the newline (and the preceding whitespace) is not included in the string.

assert """
 "aaa"
bbb
ccc
\n\r\c\x00\t
""" == " \"aaa\"\nbbb\nccc\n\\n\\r\\c\\x00\\t\n"

Raw string literals

assert r"C:\texts\text.txt" == "C:\\texts\\text.txt"
assert r"a""b" == "a\"b"

Generalized raw string literals

identifier"string literal"
is a shortcut for
identifier(r"string literal")

identifier"""string literal"""
is a shortcut for
identifier("""string literal""")

Character literals

Character literals are enclosed in single quotes ''. Newline (\n) is not allowed as it may be wider than one character (often it is the pair CR/LF for example).

A character is not an Unicode character but a single byte.Nim can thus support array[char, int] or set[char] efficiently as many algorithms rely on this feature.

Numerical constants

var
  i = 3  # signed int
  i8 = 3'i8  # signed int8
  i16 = 3'i16  # signed int16
  i32 = 3'i32  # signed int32
  i64 = 3'i64  # signed int64

  ui = 3'u  # unsigned int
  ui8 = 3'u8  # unsigned int8
  ui16 = 3'u16  # unsigned int16
  ui32 = 3'u32  # unsigned int32
  ui64 = 3'u64  # unsigned int64

  f = 0.0  # float
  f32 = 0.0'f32  # float32
  f64 = 0.0'f64  # float6

assert 0b11111111 == 255
assert 0o377 == 255
assert 0xff == 255
assert 0b0_10001110100_0000101001000111101011101111111011000101001101001001'f64 == 1.7282561e+35
assert 1_999_999 == 1999999

Size of int/uint is "size_t".

Operators

=     +     -     *     /     <     >
@     $     ~     &     %     |
!     ?     ^     .     :     \
and or not xor shl shr div mod in notin is isnot of

Other tokens

Types

  • ordinal types
    • integer (except for uint/uint64)
    • bool
    • char
    • enum
    • and subranges thereof
  • float
  • string
  • structured types
    • array
    • seq
    • set
    • openArray
    • varargs
    • tuple
    • object
  • ref (ptr) type
  • procedural type
  • generic type

Ordinal types

  • countable and ordered
  • inc(), dec()
  • ord(), low(), high(), pred(), succ()
var
  i = 0

inc(i)
assert i == 1
dec(i)
assert i == 0

i = high(int)
assert i == high(i)
try:
  inc(i)
except OverFlowError:
  # Error: unhandled exception: over- or underflow [OverflowError]
  echo("OverFlowError:", getCurrentExceptionMsg())

i = low(int)
assert i == low(i)
try:
  dec(i)
except OverFlowError:
  # Error: unhandled exception: over- or underflow [OverflowError]
  echo("OverFlowError:", getCurrentExceptionMsg())


import unsigned
# some ops requires unsigned module

var
  u:uint = 0

inc(u)
assert u == 1
dec(u)
assert u == 0

#u = low(uint)
#u = low(u)
# static error: invalid argument for `low`

#u = high(uint)
#u = high(u)
# static error: invalid argument for `high`

u = uint(high(int) * 2 + 1)
inc(u)
assert u == 0
dec(u)
assert u == uint(high(int) * 2 + 1)


var
  c = 'm'

assert ord(c) == 109
assert chr(109) == c
assert pred('m') == 'l'
assert succ('m') == 'n'

inc(c)
assert c == 'n'
dec(c)
assert c == 'm'

c = low(c)
assert c == '\0'
try:
  dec(c)
except OverFlowError:
  # Error: unhandled exception: over- or underflow [OverflowError]
  echo("OverFlowError:", getCurrentExceptionMsg())

c = high(c)
assert c == '\255'
try:
  inc(c)
except OverFlowError:
  # Error: unhandled exception: over- or underflow [OverflowError]
  echo("OverFlowError: ", getCurrentExceptionMsg())

Pre-defined integer types

import unsigned

var
  i: int
  ui: uint

assert i == 0
assert ui == 0

Subrange type

A subrange type is a range of values from an ordinal type(the base type). To define a subrange type, one must specify it's limiting values: the lowest and highest value of the type.
A subrange type of a base ordinal type which can only hold the lowest value to highest value. Assignments from the base ordinal type to one of its subrange types (and vice versa) are allowed.

A subrange type has the same size as its base type (int in the example).

type
  IntSubRange = range[-10 .. 10]
  ChrSubRange = range['a' .. 'c']

  Direction {.pure.} = enum
    north,
    south,
    east,
    west,
  EnumSubRange = range[Direction.north .. Direction.east]

var
  s_i: IntSubRange
  s_c: ChrSubRange
  s_e: EnumSubrange
  i: int = s_i

assert s_i == 0
assert s_c == '\x0'
assert s_e == Direction.north
s_i = -10
s_c = 'b'
s_e = Direction.south
#s_i = -11
# static error: conversion from int literal(-11) to intSubRange is invalid

try:
  dec(s_i)
except OverFlowError:
  # Error: unhandled exception: over- or underflow [OverflowError]
  echo("OverFlowError: ", getCurrentExceptionMsg())

Nim requires interval arithmetic for subrange types over a set of built-in operators that involve constants: x %% 3 is of type range[0..2]. The following built-in operators for integers are affected by this rule: -, +, *, min, max, succ, pred, mod, div, %%, and (bitwise and).

Bitwise and only produces a range if one of its operands is a constant x so that (x+1) is a number of two. (Bitwise and is then a %% operation.)

case (x and 3) + 7
of 7: echo "A"
of 8: echo "B"
of 9: echo "C"
of 10: echo "D"
# note: no ``else`` required as (x and 3) + 7 has the type: range[7..10]

Pre-defined floating point types

Arithmetic performed on floating point types follows the IEEE standard. Integer types are not converted to floating point types automatically and vice versa.

The IEEE standard defines five types of floating-point exceptions:

  • Invalid: operations with mathematically invalid operands, for example 0.0/0.0, sqrt(-1.0), and log(-37.8).
  • Division by zero: divisor is zero and dividend is a finite nonzero number, for example 1.0/0.0.
  • Overflow: operation produces a result that exceeds the range of the exponent, for example MAXDOUBLE+0.0000000000001e308.
  • Underflow: operation produces a result that is too small to be represented as a normal number, for example, MINDOUBLE * MINDOUBLE.
  • Inexact: operation produces a result that cannot be represented with infinite precision, for example, 2.0 / 3.0, log(1.1) and 0.1 in input.

The IEEE exceptions are either ignored at runtime or mapped to the Nim exceptions:

  • FloatInvalidOpError
  • FloatDivByZeroError
  • FloatOverflowError
  • FloatUnderflowError
  • FloatInexactError

These exceptions inherit from the FloatingPointError base class.

Nim provides the pragmas NaNChecks and InfChecks to control whether the IEEE exceptions are ignored or trap a Nim exception:

# https://akehrer.github.io/nim/2015/01/14/getting-started-with-nim-pt2.html
import math

const
  Nan = 0.0/0.0 # floating point not a number (NaN)

proc cIsNaN(x: float): cint {.importc: "isnan", header: "<math.h>".}
  ## returns non-zero if x is not a number

proc cIsInf(x: float): cint {.importc: "isinf", header: "<math.h>".}
  ## returns non-zero if x is infinity

proc isNaN*(x: float): bool =
  ## converts the integer result from cIsNaN to a boolean
  if cIsNaN(x) != 0.cint:
    true
  else:
    false

proc isInf*(x: float): bool =
  ## converts the integer result from cIsInf to a boolean
  if cIsInf(x) != 0.cint:
    true
  else:
    false


var
  a:float
  b = 1.0

assert a == 0.0
echo(0.0 / 0.0)  # --> nan
echo(1.0 / 0.0)  # --> inf
echo(a / a)  # --> -nan
echo(b / a)  # --> inf

{.push nanChecks: on, infChecks: on.}
echo(0.0 / 0.0)
echo(1.0 / 0.0)

try:
  echo a / a
except FloatInvalidOpError:
  # Error: unhandled exception: FPU operation caused a NaN result [FloatInvalidOpError]
  echo("FloatInvalidOpError: ", getCurrentExceptionMsg())

try:
  echo b / a
except FloatOverflowError:
  # Error: unhandled exception: FPU operation caused an overflow [FloatOverflowError]
  echo("FloatOverflowError: ", getCurrentExceptionMsg())
{.pop.}

In the current implementation FloatDivByZeroError and FloatInexactError are never raised. FloatOverflowError is raised instead of FloatDivByZeroError. There is also a floatChecks pragma that is a short-cut for the combination of NaNChecks and InfChecks pragmas. floatChecks are turned off as default.

The only operations that are affected by the floatChecks pragma are the +, -, *, / operators for floating point types.

An implementation should always use the maximum precision available to evaluate floating pointer values at compile time; this means expressions like 0.09'f32 + 0.01'f32 == 0.09'f64 + 0.01'f64 are true.

Boolean type

assert ord(false) == 0
assert ord(true) == 1

Character type

Nim can support array[char, int] or set[char] efficiently as many algorithms rely on this feature.

var
  c: char
assert c == '\x0'
var
  char_index_array: array[char, int]

assert char_index_array.len == 256
assert char_index_array['a'] == 0
char_index_array['v'] = 1
assert char_index_array['v'] == 1

var
  char_set: set[char] = {'a', 'a', 'b'}

assert char_set == {'a', 'b'}
var
  char_buffer: array[0 .. 255, char]

assert char_buffer.len == 256
assert char_buffer == ['\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0']

char_buffer[0] = 'a'

assert cstring(char_buffer).len == 1
echo cstring(char_buffer)
# --> a

Enumeration type

Enumeration types define a new type whose values consist of the ones specified. The values are ordered.

type
  Direction = enum
    north,
    south,
    east,
    west,

assert north == Direction.north
assert north < Direction.south

assert ord(Direction.north) == 0
assert ord(north) == 0
assert ord(south) == 1
assert ord(east) == 2
assert ord(west) == 3

assert pred(east) == south
assert succ(east) == west

var
  e: Direction

assert e == north
e = east

inc(e)
assert e == west
dec(e)
assert e == east
assert low(Direction) == north
assert low(e) == north
assert high(Direction) == west
assert high(e) == west

assert Direction(0) == north
assert Direction(1) == south
assert Direction(2) == east
assert Direction(3) == west

var
  arr_e0: array[Direction, float]
  arr_e1: array[north .. west, float]

assert arr_e0[east] == 0.0
assert arr_e1[east] == 0.0
arr_e0[east] = 1.1


type
  ScopedEnum {.pure.} = enum
    val_a,
    val_b,

# static error: undeclared identifier: 'val_a'
#echo val_a

The fields of enum types can be assigned an explicit ordinal value. However, the ordinal values have to be in ascending order. A field whose ordinal value is not explicitly given is assigned the value of the previous field + 1.

An explicit ordered enum can have holes. However, it is then not an ordinal anymore, so it is not possible to use these enums as an index type for arrays. The procedures inc, dec, succ and pred are not available for them either.

type
  Status = enum
    ok = 200,
    created,
    accepted = "Accepted",
    multiple_sources = 300,
    moved_permanently = "Moved Permanently",
    bad_request = 400,
    internal_server_error = (500, "Internal Server Error")

assert ord(ok) == 200
assert ord(created) == 201
assert ord(accepted) == 202

# static error: enum 'Status' has holes
#var
#  x: array[ok .. accepted, int]
#  y: array[Status, int]

var
  status = ok

inc(status)
assert status == created
inc(status)
assert status == accepted
inc(status)
assert status != multiple_sources

echo(status)
# --> 203 (invalid data!)

String type

Zero(Null)-terminated and have a length field. The length never counts the terminating zero. The assignment operator for strings always copies the string. The & operator concatenates strings.

Strings are compared by their lexicographical order. All comparison operators are available. Strings can be indexed like arrays (lower bound is 0).

var
  s: string

assert s.isNil
s = "this"  # assignment op does COPY the string

var
  conc = s & " is"  # concat

assert s[0] == 't'
# static error
#assert s[0] == "t"
s[0] = 'T'
# static error
#s[0] = "T"

assert s[0 .. 1] == "Th"
s.add(" ")
s.add("is a string")
assert s == "This is a string"
assert s[low(s) .. high(s)] == "This is a string"

try:
  echo s[100]
except IndexError:
  # Error: unhandled exception: index out of bounds [IndexError]
  echo("IndexError: ", getCurrentExceptionMsg())
var
  s = "0123456789"

assert s.len == 10
assert s[0] == '0'
assert s[^0] == '\x0'
assert s[^1] == '9'
assert s[1 .. 1] == "1"
assert s[1 .. 3] == "123"
assert s[^1 .. ^1] == "9"
assert s[^3 .. ^1] == "789"
assert s[^1 .. ^2] == ""
assert s[0 .. ^0] == "0123456789"
assert s[0 .. ^1] == "0123456789"
s[0] = 'a'
assert s == "a123456789"
s[0 .. 0] = "a" 
assert s == "a123456789"
s[0 .. 2] = "abc"
assert s == "abc3456789"
s[^3 .. ^1] = "xyz"
assert s == "abc3456xyz"
s[0 .. 2] = "a"
assert s == "a3456xyz"
s[0 .. 2] = ""
assert s == "56xyz"
s[0 .. 0] = "012345"
assert s == "0123456xyz"
s[0 .. 2] = ""
assert s == "3456xyz"
s[^3 .. ^1] = ""
assert s == "3456"
s.setLen(1)
assert s.len == 1
assert s == "3"

Per convention, all strings are UTF-8 strings, but this is not enforced. For example, when reading strings from binary files, they are merely a sequence of bytes. The index operation s[i] means the i-th char of s, not the i-th unichar.

cstring type

A pointer to zero-terminated char array(char* in c). No boundChecks. The index operation is unsafe.

A Nim string is implicitly convertible to cstring for convenience. If a Nim string is passed to a C-style variadic proc, it is implicitly converted to cstring too:

Even though the conversion is implicit, it is not safe: The garbage collector does not consider a cstring to be a root and may collect the underlying memory. However in practice this almost never happens as the GC considers stack roots conservatively. One can use the builtin procs GC_ref and GC_unref to keep the string data alive for the rare cases where it does not work.

A $ proc is defined for cstrings that returns a string. Thus to get a nim string from a cstring:

proc printf(formatstr: cstring) {.importc:
  "printf", varargs,
  header: "<stdio.h>".}

printf("This works as expected %s", "by printf in stdio")

var
  str: string = "hello"
  cstr: cstring

assert cstr.isNil

cstr = str

var
  str_back: string = $cstr
var
  # `system.cstringArray` is equal to `ptr array [0..ArrayDummySize, cstring]`
  cstring_arr: cstringArray = allocCStringArray(["abc"])

cstring_arr[1] = "def"
cstring_arr[2] = "ghi"
assert cstringArrayToSeq(cstring_arr) == @["abc", "def", "ghi"]

dealloc(cstring_arr)

# if the members of a cstringArray are allocated (for example, by c), call `deallocCStringArray()`
#deallocCStringArray(cstring_arr)

Structured types

Arrays, sequences, tuples, objects and sets belong to the structured types.

Array and sequence types

array

  • element: homogeneous
  • length: fixed
  • index type: ordinal type
  • expression constructor: []
  • boundsCheck: yes(compile-time)
import typetraits

var
  arr_obj: array[0 .. 5, int]
  arr_ref: ref array[0 .. 5, int]

assert arr_obj.type.name == "array[0..5, int]"
echo repr(arr_obj)
# --> [0, 0, 0, 0, 0, 0]

assert arr_ref.isNil
new(arr_ref)
assert (not arr_ref.isNil)
assert arr_ref.type.name == "ref array[0..5, int]"
echo repr(arr_ref)
# --> ref 0x7fda81f0b0d0 --> [0, 0, 0, 0, 0, 0]
var
  ii_arr0: array[int, int]
  # too big array

assert low(ii_arr0) == low(int)
assert high(ii_arr0) == high(int)
assert len(ii_arr0) == 0
assert ii_arr0[0] == 0
ii_arr0[0] = 0
assert ii_arr0.len == 0


var
  i16i_arr: array[int16, int]

assert low(i16i_arr) == low(int16)
assert high(i16i_arr) == high(int16)
assert len(i16i_arr) == 65536


var
  ii_arr1: array[0 .. 3, int]

assert low(ii_arr1) == 0
assert high(ii_arr1) == 3
assert len(ii_arr1) == 4
assert ii_arr1[0] == 0
ii_arr1 = [0, 1, 2, 3]


var
  ci_arr: array[char, int]

assert low(ci_arr) == low(char)
assert high(ci_arr) == high(char)
assert len(ci_arr) == 256
assert ci_arr['a'] == 0
ci_arr['v'] = 1


type
  Direction = enum
    north,
    south,
    east,
    west,

var
  ei_arr0:  array[Direction, int]
  ei_arr1:  array[north .. west, int]

assert low(ei_arr0) == north
assert high(ei_arr0) == west
assert len(ei_arr0) == 4
assert ei_arr0[north] == 0
ei_arr0[north] = 1

seq

  • element: homogeneous
  • length: dynamic
  • index type: ordinal type. From 0 to len(S) - 1
  • boundsCheck: yes
  • expression constructor: @[] (the array constructor [] in conjunction with the array to sequence operator @)
  • space alloc:
    • proc system.newSeq[T](s: var seq[T]; len: Natural)
    • proc system.newSeq[T](len = 0.Natural): seq[T]

newSeq creates a new sequence of type seq[T] with length len. This is equivalent to s = @[]; setlen(s, len), but more efficient since no reallocation is needed.

var
  seq_ref0: ref seq[int]

assert seq_ref0.isNil
new(seq_ref0)
assert (not seq_ref0.isNil)
echo repr(seq_ref0)
# --> ref 0x7f180287b050 --> nil
assert seq_ref0[].isNil


var
  seq_obj = newSeq[int]()
  seq_ref1: ref seq[int]

assert seq_ref1.isNil
new(seq_ref1)
assert (not seq_ref1.isNil)
echo repr(seq_ref1)
# --> ref 0x7f180287b068 --> nil
seq_ref1[] = seq_obj
echo repr(seq_obj)
# --> 0x7f180287c050[]
echo repr(seq_ref1)
# --> ref 0x7f180287b068 --> 0x7f180287c070[]
var
  seq_0: seq[int]
  seq_1 = @[0, 1, 2]

assert seq_0.isNil
seq_0 = @[0, 1, 2]
assert (not seq_0.isNil)
assert low(seq_0) == 0
assert high(seq_0) == 2
assert len(seq_0) == 3
assert seq_0 & seq_1 == @[0, 1, 2, 0, 1, 2]
assert seq_0.pop() == 2
seq_0.add(1)
assert seq_0 == @[0, 1, 1]


var
  seq_2: seq[int]

assert seq_2.isNil
newSeq(seq_2, 0)
assert (not seq_2.isNil)
assert len(seq_2) == 0


var
  seq_3 = newSeq[int]()

assert (not seq_3.isNil)
assert len(seq_3) == 0


var
  seq_4 = newSeq[int](3)

assert (not seq_4.isNil)
assert len(seq_4) == 3

proc shallow[T](s: var seq[T]) marks a sequence s as shallow. Subsequent assignments will not perform deep copies of s. This is only useful for optimization purposes.

var
  s0 = @[0, 0, 0]

var
  s1 = s0

shallow(s0)

var
  s2 = s0
  s3 = s0[0..2]

inc s0[0]
s0.add(3)
assert s0 == @[1, 0, 0, 3]
assert s1 == @[0, 0, 0]
assert s2 == @[1, 0, 0]
assert s3 == @[0, 0, 0]

openArray

{.push boundChecks: off.}
proc to_str_seq(a: openArray[int]): seq[string] =
  #var
  #  result = newSeq[string](len(a))
  newSeq(result, len(a))
  for i, it in a:
    result[i] = $it
{.pop.}

assert to_str_seq([1,2,3]) == @["1", "2", "3"]


{.push boundChecks: off.}
proc to_reversed_seq[T](a: openArray[T]): seq[T] =
  var
    li = 0
    hi = high(a)
  newSeq(result, hi + 1)
  while hi >= 0:
    result[hi] = a[li]
    dec(hi)
    inc(li)
{.pop.}

var
  arr = [1, 2, 3]
  s = @[1, 2, 3]

assert arr.to_reversed_seq == @[3, 2, 1]
assert s.to_reversed_seq == @[3, 2, 1]

varargs

proc anyFileWriteln0(f: File, sargs: varargs[string]): void =
  for s in sargs:
    write(f, s)
  write(f, "\n")

anyFileWriteln0(stdout, "abc", "def", "xyz")
# = anyFileWriteln0(stdout, ["abc", "def", "xyz",])


proc anyFileWriteln1(f: File, sargs: varargs[string, `$`]): void =
  for s in sargs:
    write(f, s)
  write(f, "\n")


anyFileWriteln1(stdout, 123, "def", 4.0)
# = anyFileWriteln1(stdout, [$123, "def", $4.0])


proc toS(x): string = $x


proc anyFileWriteln2(f: File, sargs: varargs[string, toS]): void =
  for s in sargs:
    write(f, s)
  write(f, "\n")


anyFileWriteln2(stdout, 123, 456, 5)
# = anyFileWriteln2(stdout, [toS(123), toS(456), toS(5)])


proc takeV[T](x: varargs[T]): void =
  for s in x:
   echo($s)


takeV([1, 2, 3])

varargs[expr] is treated specially: It matches a variable list of arguments of arbitrary type but always constructs an implicit array. This is required so that the builtin echo proc does what is expected:

proc echo*(x: varargs[expr, `$`]) {...}

echo(@[1, 2, 3])
# prints "@[1, 2, 3]" and not "123"

Tuples and object types

tuple

The order of the fields in the constructor must match the order of the tuple's definition. Different tuple-types are equivalent if they specify the same fields of the same type in the same order. The names of the fields also have to be identical.

The assignment operator for tuples copies each component.

type
  Person0 = tuple[
    name: string,
    age: Natural,
  ]

var
  p0: Person0

p0 = (name: "Peter", age: 20.Natural)  # assinment op does COPY each component
p0 = ("Peter", 20.Natural)


# tuples in a type section can also be defined with indentation instead of []:
type
  Person1 = tuple
    name: string
    age: Natural

var
  p1: Person1 = (name: "Peter", age: 20.Natural)
  p2: Person1 = (name: "Adam", age: 30.Natural)


assert p0 == p1
assert p0 != p2
assert p0 == (name: "Peter", age: 20.Natural)


# These are invalid because illegal recursion in the type
# type
  # MyTuple0 = tuple[a: MyTuple0]
  # MyTuple1 = tuple[a: ref MyTuple1]

import tables

var
  field_seq: seq[string] = @[]
  field_pairs: Table[string, string] = initTable[string, string]()

for v in p0.fields:
  field_seq.add($v)

assert field_seq == @["Peter", "20"]

field_seq.setLen(0)

for v0, v1 in fields(p0, p2):
  field_seq.add($v0)
  field_seq.add($v1)

assert field_seq == @["Peter", "Adam", "20", "30"]

for k, v in p0.fieldPairs:
  field_pairs.add($k, $v)

assert field_pairs == {"name": "Peter", "age": "20"}.toTable

object

The default assignment operator for objects copies each component.

In contrast to tuples, different object types are never equivalent. Objects that have no ancestor are implicitly final and thus have no hidden type field. One can use the inheritable pragma to introduce new object roots apart from system.RootObj.

type
  PersonObj* {.inheritable.} = object
    name*: string
    age*: Natural
  Person* = ref PersonObj

  SubPersonObj* = object of PersonObj
  SubPerson* = ref SubPersonObj

  AltPersonObj* {.inheritable.} = object
    name*: string
    age*: Natural
  AltPerson* = ref AltPersonObj

  StudentObj* = object of PersonObj
    id: int
  Student* = ref StudentObj

var
  pobj: PersonObj
  p0, p1: Person
  subp: SubPerson
  altp: AltPerson
  s: Student

echo(repr(pobj))
# --> [name = nil, age = 0]
assert p0.isNil
p0 = Person(name: "Peter", age: 20.Natural)  # assignment op does COPY each component
p1 = Person(name: "Peter", age: 20.Natural)
subp = SubPerson(name: "Peter", age: 20.Natural)
altp = AltPerson(name: "Peter", age: 20.Natural)
s = Student(name: "Peter", age: 20.Natural, id: 1)

assert p0 of Person
assert p0 of PersonObj
assert p0[] of Person
assert p0[] of PersonObj
assert p0.type is Person
assert p0[].type is PersonObj

assert subp of SubPerson
assert subp of SubPersonObj
assert subp[] of SubPerson
assert subp[] of SubPersonObj
assert subp.type is SubPerson
assert subp[].type is SubPersonObj

assert subp of Person
assert subp of PersonObj
assert subp[] of Person
assert subp[] of PersonObj
assert subp.type is Person
assert subp[].type is PersonObj

assert s of Person
assert s of PersonObj
assert s[] of Person
assert s[] of PersonObj
assert s.type is Person
assert s[].type is PersonObj

assert p0 != p1
assert p0[] == p1[]
assert p0 != subp
assert p0[] == subp[]
#assert subp[] != p0[]
# static error: type mismatch: got(SubPersonObj, PersonObj)

#assert p0 != altp
# static error: type mismatch: got(Person, AltPerson)

#assert p0[] == altp[]
# static error: type mismatch: got(Person, AltPerson)
assert p0 != s
#assert s != p0
# static eror: type mismatch: got(Student, Person)

assert p0[] == s[]
#assert s[] == p0[]
# static eror: type mismatch: got(StudentObj, PersonObj)
assert s.name == "Peter"
assert s.age == 20
assert s.id == 1


proc newStudent(name: string, age: int, id: int): Student =
  new(result)
  result.name = name
  result.age = age.Natural
  result.id = id


var
  s1 = newStudent("Name", 20, 1)
  s2 = newStudent("Name", 20, 1)

assert s1 != s2
assert s1[] == s2[]


var
  s3: Student

new(s3)
s3.name = "Name"
s3.age = 20
s3.id = 1


import tables

p1.name = "Adam"
p1.age = 30
subp.name = "Adam"
subp.age = 30

var
  field_seq: seq[string] = @[]
  field_pairs: Table[string, string] = initTable[string, string]()

for v in p0[].fields:
  field_seq.add($v)

assert field_seq == @["Peter", "20"]

field_seq.setLen(0)

for v0, v1 in fields(p0[], p1[]):
  field_seq.add($v0)
  field_seq.add($v1)

assert field_seq == @["Peter", "Adam", "20", "30"]

for k, v in p0[].fieldPairs:
  field_pairs.add($k, $v)

assert field_pairs == {"name": "Peter", "age": "20"}.toTable

field_seq.setLen(0)
# static error: type mismatch: got(SubPersonObj) but expected 'PersonObj'
#for v0, v1 in fields(p0[], subp[]):
#  field_seq.add($v0)
#  field_seq.add($v1)

# static error: type mismatch: got(StudentObj) but expected 'PersonObj'
#for v0, v1 in fields(p0[], s[]):
#  field_seq.add($v0)
#  field_seq.add($v1)

https://github.com/nim-lang/Nim/issues/3012
https://github.com/nim-lang/Nim/issues/2926

type
  A {.inheritable.} = object
  B = ref object {. inheritable .}
  C {.inheritable.} = ref object
  D = object {.inheritable.}

type
  AA = ref object of A
  BB = ref object of B
  #CC = ref object of C
  # Error: inheritance only works with non-final objects
  DD = ref object of D

Object construction

Objects can also be created with an object construction expression that has the syntax T(fieldA: valueA, fieldB: valueB, ...) where T is an object type or a ref object type:

var
  student_obj = StudentObj(name: "Anton", age: 5, id: 3)
  student = Student(name: "Anton", age: 5, id: 3)

For a ref object type system.new is invoked implicitly.

Object variants

An advantage to an object hierarchy is that no casting between different object types is needed. Yet, access to invalid object fields raises an exception.

type
  NodeKind = enum
    nkInt,
    nkFloat,
    nkString,
    nkAdd,
    nkSub,
    nkIf,

  Node = ref NodeObj

  NodeObj = object
    case kind: NodeKind
    of nkInt:
      valInt: int
    of nkFloat:
      valFloat: float
    of nkString:
      valString: string
    of nkAdd, nkSub:
      opLeft, opRight: Node
    of nkIf:
      condition, thenPart, elsePart: Node


var
  n = Node(kind: nkIf, condition: nil)


n.thenPart = Node(kind: nkFloat, valFloat: 2.0)

try:
  n.valString = ""
except FieldError:
  # Error: unhandled exception: valString is not accessible [FieldError]
  echo(getCurrentExceptionMsg())


try:
  n.kind = nkInt
except FieldError:
  # Error: unhandled exception: assignment to discriminant changes object branch [FieldError]
  echo(getCurrentExceptionMsg())



var x: Node = Node(
  kind: nkAdd,
  opLeft: Node(kind: nkInt, valInt: 3),
  opRight: Node(kind: nkInt, valInt: 2),
)

x.kind = nkSub

In the example the kind field is called the discriminator: For safety its address cannot be taken and assignments to it are restricted: The new value must not lead to a change of the active object branch. For an object branch switch system.reset has to be used.

Set type

The set's basetype can only be an ordinal type. The reason is that sets are implemented as high performance bit vectors.

type
  CharSet = set[char]

var
  cs0: CharSet = {'a' .. 'z', 'A' .. 'Z', '0' .. '9'}
  cs1: CharSet = {'a'.. 'd'}
  cs2: CharSet = {'c'.. 'f'}
  cs3: CharSet = {'c', 'd'}

assert cs1 + cs2 == {'a' .. 'f'}
assert cs1 - cs2 == {'a', 'b'}
assert cs1 * cs2 == {'c', 'd'}
assert cs1 != cs2
assert cs1 >= cs3
assert cs1 > cs3
assert 'a' in cs1
assert cs1.contains('a')
assert 'e' notin cs1
assert card(cs1) == 4
incl(cs1, 'a')
assert cs1 == {'a'.. 'd'}
incl(cs1, 'e')
assert cs1 == {'a'.. 'e'}
excl(cs1, 'e')
assert cs1 == {'a'.. 'd'}
operation meaning
A + B union of two sets
A * B intersection of two sets
A - B difference of two sets (A without B's elements)
A == B set equality
A <= B subset relation (A is subset of B or equal to B)
A < B strong subset relation (A is a real subset of B)
e in A set membership (A contains element e)
e notin A A does not contain element e
contains(A, e) A contains element e
card(A) the cardinality of A (number of elements in A)
incl(A, elem) same as A = A + {elem}
excl(A, elem) same as A = A - {elem}

Reference and pointer type

The . (access a tuple/object field operator) and [] (array/string/sequence index operator) operators perform implicit dereferencing operations for reference types:

To allocate a new traced object, the built-in procedure new has to be used. To deal with untraced memory, the procedures alloc, dealloc and realloc can be used.

Special care has to be taken if an untraced object contains traced objects like traced references, strings or sequences: in order to free everything properly, the built-in procedure GCunref has to be called before freeing the untraced memory manually:

If a reference points to nothing, it has the value nil.

type
  Node = ref NodeObj
  
  NodeObj = object
    left, right: Node
    data: int

var
  x: Node
  y: Node
  z: Node

new(x)
new(y)
new(z)
x.data = 9
x.left = y
x.right = z

proc get_data(x: NodeObj): int =
  return x.data

# static error
#echo(get_data(x))
assert get_data(x[]) == 9

{. experimental .}
assert get_data(x) == 9
type
  # the object type is anonymous
  Node = ref object
    left, right: Node
    data: int

var
  n: Node
new(n)
echo(repr(n))
# --> ref 0x7f9e92e4c050 --> [left = nil, right = nil, data = 0]
echo(repr(n[]))
# --> [left = nil, right = nil, data = 0]
type
  Data = tuple[
    x, y: int,
    s: string,
  ]

# allocate memory for Data on the heap
var
  # Without the GCunref call the memory allocated for the d.s string
  # would never be freed. The example also demonstrates two important
  # features for low level programming: the sizeof proc returns the
  # size of a type or value in bytes.
  # The cast operator can circumvent the type system: the compiler is
  # forced to treat the result of the alloc0 call (which returns an
  # untyped pointer) as if it would have the type ptr Data.
  # Casting should only be done if it is unavoidable: it breaks type
  # safety and bugs can lead to mysterious crashes.

  # Note: The example only works because the memory is initialized to
  # zero (alloc0 instead of alloc does this): d.s is thus initialized
  # to nil which the string assignment can handle.
  # One needs to know low level details like this when mixing garbage
  # collected data with unmanaged memory.
  d = cast[ptr Data](alloc0(sizeof(Data)))

# create a new string on the garbage collected heap
d.s = "abc"

# tell the GC that the string is not needed anymore
GCunref(d.s)

# free the memory
dealloc(d)
type
  RequestObj {.inheritable.} = object
    body: string
  Request = ref RequestObj


proc handle_varobj(r: var RequestObj) =
  r.body = "changed by handle_varobj"


proc handle_varobj_returning(r: var RequestObj): RequestObj =
  r.body = "changed by handle_varobj_returning"
  return r


proc handle_ref(r: Request) =
  r.body = "changed by handle_ref"


var
  obj0: RequestObj = RequestObj(body: "original")
  obj1: RequestObj = RequestObj(body: "original")


assert obj0 == obj1
assert obj0.addr != obj1.addr
obj1 = obj0
assert obj0 == obj1
assert obj0.addr != obj1.addr
obj1.body = "x"
assert obj0 != obj1
assert obj0.body != obj1.body
handle_varobj(obj0)
assert obj0.body == "changed by handle_varobj"
obj1 = handle_varobj_returning(obj0)
assert obj0.body == obj1.body
assert obj0 == obj1
assert obj0.addr != obj1.addr


var
  ref0: Request
  ref1: Request

new(ref0)
new(ref1)
assert ref0 != ref1
assert ref0[] == ref1[]
ref1 = ref0
ref0.body = "original"
assert ref0.body == ref1.body
assert ref0 == ref1
assert ref0.addr != ref1.addr
assert ref0[] == ref1[]
assert ref0[].addr == ref1[].addr
type
  Obj = object
    kind: string
  ObjRef = ref Obj
  ObjPtr = ptr Obj

var
  obj = Obj(kind: "Obj")
  obj_ref, obj_ref1 : ObjRef
  obj_ptr, obj_ptr1 : ObjPtr


new(obj_ref)
obj_ref.kind = "ObjRef"

obj_ref1 = ObjRef(kind: "ObjRef") 

obj_ptr = cast[ptr Obj](alloc0(sizeof(Obj)))
obj_ptr.kind = "ObjPtr"

obj_ptr1 = create(Obj)
obj_ptr1.kind = "ObjPtr"


echo("======== object ========")
echo("obj = ", obj)
# --> (kind: Obj)
echo("obj = ", repr obj)
# --> [kind = 0x7f3ee921f050"Obj"]
echo("obj.addr = ", repr obj.addr)
# --> ref 0x624170 --> [kind = 0x7f3ee921f050"Obj"]
echo("obj.addr.pointer = ", repr obj.addr.pointer)
# --> 0x624170
assert obj.addr[] == obj
assert cast[ObjPtr](obj.addr.pointer)[] == obj
assert cast[ptr Obj](obj.addr.pointer)[] == obj

echo("======== ref ========")
echo("obj_ref = ", repr obj_ref)
# --> ref 0x7f5d71971050 --> [kind = 0x7f5d71970078"ObjRef"]
echo("obj_ref.addr = ", repr obj_ref.addr)
# --> ref 0x624168 --> ref 0x7f5d71971050 --> [kind = 0x7f5d71970078"ObjRef"]
echo("obj_ref.addr.pointer = ", repr obj_ref.addr.pointer)
# --> 0x624168
assert obj_ref.addr[] == obj_ref
assert obj_ref.addr[][] == obj_ref[]
assert cast[ptr ref Obj](obj_ref.addr)[] == obj_ref
assert cast[ptr ref Obj](obj_ref.addr)[][] == obj_ref[]

echo("======== ptr ========")
echo("obj_ptr = ", repr(obj_ptr))
# ref 0x7f5d71971080 --> [kind = 0x7f5d719700c8"ObjPtr"]
echo("obj_ptr.pointer = ", repr(obj_ptr.pointer))
# 0x7f5d71971080
assert cast[ptr Obj](obj_ptr.pointer) == obj_ptr
assert cast[ptr Obj](obj_ptr.pointer)[] == obj_ptr[]



proc check(x: pointer): string =
  "pointer"


proc check(x: ptr int): string =
  "ptr int"

assert check(create(int)) == "ptr int"

assert check(create(int).pointer) == "pointer"

# `ptr` is implicitly convertible to `pointer`
assert check(create(char)) == "pointer"


proc check(x: ptr): string =
  "ptr"

assert check(create(char)) == "ptr"


GCunref(obj_ptr.kind)
dealloc(obj_ptr)
GCunref(obj_ptr1.kind)
dealloc(obj_ptr1)

not nil annotation

All types for that nil is a valid value can be annotated to exclude nil as a valid value with the not nil annotation:

type
  Proc = ref ProcObj not nil
  ProcObj = (proc (x, y: int)) not nil

proc call_proc(x: ProcObj): string =
  "called"

proc call_proc(x: Proc): string =
  "called"

proc deal_str(s: string not nil): string =
  "called"

var
  proc_obj: ProcObj
  proc_ref: Proc
  s: string

# static error
#call_proc(nil)

assert proc_obj.isNil
assert call_proc(proc_obj) == "called"

assert proc_ref.isNil
assert call_proc(proc_ref) == "called"

# static error
#deal_str(nil)

assert s.isNil
#discard deal_str(s)
# Error: cannot prove 's' is not null

Memory regions

Procedural type

type
  Event {.pure.} = enum
    starting,
    started,
    stopping,
    stopped,
    restarting,
    restarted,

proc callback_event(
  e: Event,
  callback: proc(e: Event),
) =
  callback(e)

proc callback_event_varargs(
  e: Event,
  callbacks: varargs[proc(e: Event) {.nimcall.}],
) =
  for c in callbacks:
    c(e)

proc callback_event_openArray(
  e: Event,
  callbacks: openArray[proc(e: Event) {.nimcall.}],
) =
  for c in callbacks:
    c(e)

proc echo_callback0(
  e: Event,
) =
  echo("Event: ", e)

proc echo_callback1(
  e: Event,
): void {.nimcall.} =
  echo("Event: ", e)

callback_event(Event.started, echo_callback0)
callback_event(Event.started, echo_callback1)
callback_event_varargs(Event.started, echo_callback0, echo_callback1)
callback_event_openArray(Event.started, @[echo_callback0, echo_callback1])


type
  Request = ref object
    data: string

proc handle_request(
  req: var Request,
  c: proc(req: var Request),
) =
  c(req)

proc addstr_handler(
  req: var Request,
): void =
  req.data &=" added"
  discard

var
  req = Request(data: "data")

handle_request(req, addstr_handler)
assert req.data == "data added"
type
  Event {.pure.} = enum
    starting,
    started,
    stopping,
    stopped,
    restarting,
    restarted,

proc callback_one(
  e: Event,
  callback: proc(e: Event),
) =
  callback(e)

proc callback_varargs(
  e: Event,
  callbacks: varargs[proc(e: Event) {.closure.}],
) =
  for cb in callbacks:
    cb(e)

proc callback_openArray(
  e: Event,
  callbacks: openArray[proc(e: Event) {.closure.}],
) =
  for cb in callbacks:
    cb(e)

proc echo_cb0(e: Event) =
  echo("Event: ", e)

proc echo_cb1(e: Event) {.closure.} =
  echo("Event: ", e)

callback_one(Event.started, echo_cb0)
# static error (different calling convention)
#callback_varargs(Event.started, echo_cb0)
#callback_openArray(Event.started, echo_cb0)
callback_varargs(Event.started, echo_cb1)
callback_openArray(Event.started, [echo_cb1])
callback_openArray(Event.started, @[echo_cb1])


type
  Request = ref object
    data: string

proc handle_request(
  r: var Request,
  handler: proc(r: var Request): string
): string =
  return handler(r)

proc change_data_handler(
  r: var Request,
): string {.procvar.} =
  r.data &=" changed"
  return r.data

var
  r = Request(data: "data")

assert handle_request(r, change_data_handler) == "data changed"
assert r.data == "data changed"

distinct type

type
  Dollar* = distinct float

var
  d: Dollar


# static error
#echo(d + 12)

proc `$`*(x: Dollar): string =
  result = "$" & $float(x)

proc `==`*(x, y: Dollar): bool {.borrow.}
proc `<=`*(x, y: Dollar): bool {.borrow.}
proc `<`*(x, y: Dollar): bool {.borrow.}


proc `+`*(x, y: Dollar): Dollar =
  result = Dollar(float(x) + float(y))


proc `-`*(x, y: Dollar): Dollar {.borrow.}


proc `*`*(x: Dollar, y: float): Dollar {.borrow.}
proc `*`*(x: float, y: Dollar): Dollar {.borrow.}

proc `/`*(x: Dollar, y: float): Dollar {.borrow.}


assert Dollar(2) + d == Dollar(2) * 1
template add_same_types(typ: typedesc): stmt =
  proc `+`*(x, y: typ): typ {.borrow.}
  proc `-`*(x, y: typ): typ {.borrow.}

  # unary ops
  proc `+`*(x: typ): typ {.borrow.}
  proc `-`*(x: typ): typ {.borrow.}


template multiply_by_base_type(typ, base_typ: typedesc): stmt =
  proc `*`*(x: typ, y: base_typ): typ {.borrow.}
  proc `*`*(x: base_typ, y: typ): typ {.borrow.}
  proc `/`*(x: typ, y: base_typ): typ {.borrow.}


template compare_same_types(typ: typedesc): stmt =
  proc `==`*(x, y: typ): bool {.borrow.}
  proc `<`*(x, y: typ): bool {.borrow.}
  proc `<=`*(x, y: typ): bool {.borrow.}


template define_currency(typ, base_typ: expr): stmt =
  type
    typ* = distinct base_typ
  add_same_types(typ)
  multiply_by_base_type(typ, base_typ)
  compare_same_types(typ)


define_currency(Euro, float)
type
  FooObj = object
    i: int
    s: string

  BarObj {.borrow: `.`.} = distinct FooObj

  Bar = ref BarObj

var
  b: Bar


new(b)
b.i = 1
b.s = "s"
type
  AltString = distinct string


proc add *(x: var AltString, y: string) =
  ## workaround: https://github.com/nim-lang/Nim/issues/3082
  system.add(string(x), y)

proc `&=` *(x: var AltString, y: string) =
  ## workaround: https://github.com/nim-lang/Nim/issues/3082
  system.`&=`(string(x), y)


var
  a = AltString("altstring")
  b = a

assert string(a) == "altstring"
assert string(a) == string(b)
string(a)[0 .. 2] = ""
assert repr(a) != repr(b)
assert repr(a) == repr(string(a))
assert repr(a) == repr(cast[string](a))


type
  AltInt = distinct int

var
  i = AltInt(1)


int(i) += 1
assert int(i) == 2
assert repr(i) == repr(int(i))
assert repr(i) == repr(cast[int](i))

void type

proc callProc[T: int|void](p: proc (x: T), x: T) =
  when T is void:
    p()
  else:
    p(x)


proc intProc(x: int) =
  echo("called intProc")


proc emptyProc() = discard


callProc[int](intProc, 12)
callProc(intProc, 12)

callProc[void](emptyProc)
# static error: a void type can not be inferred in generic code.
#callProc(emptyProc)

Statements and Expressions

statement list expression

var str = (var x: seq[string] = @[]; for i in [0, 1, 2]: x.add($i); x)
echo str
assert str == @["0", "1", "2"]

All the other statements than list expression must be of type void. (One can use discard to produce a void type.) (;) does not introduce a new scope.

discard statement

proc p(x, y: int): int {.discardable.} =
  x + y

p(1, 2)
Type default value
any integer type 0
any float 0.0
char '\0'
bool false
ref or pointer type nil
procedural type nil
sequence nil (not @[])
string nil (not "")
tuple[x: A, y: B, ...] (default(A), default(B), ...) (analogous for objects)
array[0..., T] [default(T), ...]
range[T] default(T); this may be out of the valid range
T = enum castT; this may be an invalid value
# http://forum.nim-lang.org/t/1429/
# I'm not sure what you are expecting. {.noinit.} makes no guarantees
# about the content of a variable, and for global variables, it will
# generally result in the same initial value, anyway.
# All that {.noinit.} says is that it won't explicitly assign default
# values; the variable will still have some value.
# In the case of global variables, whatever's associated with zeroed
# memory; in the case of local variables, whatever's been on the stack
# location/in the register.
# For me, all the asserts work, and that's pretty much what I'd expect.

var
  int_0: int
  int_1 {.noInit.}: int
  float_0: float
  float_1 {.noInit.}: float
  char_0: char
  char_1 {.noInit.}: char
  string_0: string
  string_1 {.noInit.}: string
  bool_0: bool
  bool_1 {.noInit.}: bool
  arr_0: array[0..2, int]
  arr_1 {.noInit.}: array[0..2, int]
  seq_0: seq[int]
  seq_1 {.noInit.}: seq[int]
  tuple_0: tuple[id: int]
  tuple_1 {.noInit.}: tuple[id: int]
  range_0: range[0 .. 2]
  range_1 {.noInit.}: range[0 .. 2]
  proc_0: proc (x: int)
  proc_1 {.noInit.}: proc (x: int)


assert int_0 == 0
assert int_1 == 0
assert float_0 == 0.0
assert float_1 == 0.0
assert char_0 == '\x0'
assert char_1 == '\x0'
assert string_0 == nil
assert string_1 == nil
assert bool_0 == false
assert bool_1 == false
assert arr_0 == [0, 0, 0]
assert arr_1 == [0, 0, 0]
assert seq_0 == nil
assert seq_1 == nil
assert tuple_0 == (id: 0)
assert tuple_1 == (id: 0)
assert range_0 == 0
assert range_1 == 0
assert proc_0 == nil
assert proc_1 == nil


type
  Obj = object
    x: int
    y: string

  Ref = ref Obj


var
  obj_0: Obj
  obj_1 {.noInit.}: Obj
  ref_0: Ref
  ref_1 {.noInit.}: Ref


assert obj_0.x == 0
assert obj_0.y == nil
assert obj_0 == obj_1
assert ref_0 == nil
assert ref_1 == nil


proc procInt: int {.noInit.} =
  echo("int: ", result)  # => 140732219179232

assert procInt() != 0


proc procFloat: float {.noInit.} =
  echo("float: ", result)  # => 6.953095475945912e-310

assert procFloat() != 0.0


proc procChar: char {.noInit.} =
  echo("char: ", repr(result))  # => '\0'

assert procChar() == '\0'


proc procString: string {.noInit.} =
  echo("string: ", result)  # => 6.953095475945912e-310

assert procString() != nil


proc procBool: bool {.noInit.} =
  echo("bool: ", result)  # => false

assert procBool() == false


proc procArray: array[0..2, int] {.noInit.} =
  echo("array: ", repr(result))  # => [0, 0, 0]

assert procArray() == [0, 0, 0]


#proc procSeq: seq[int] {.noInit.} =
#  echo("seq: ", repr(result))

#assert procSeq() == nil


proc procTuple: (int, int) {.noInit.} =
  echo("tuple: ", result)  # => (Field0: 3, Field1: 2)

assert procTuple() != (0, 0)


proc procRange: range[0 .. 2] {.noInit.} =
  echo("range: ", result)  # => 65536

assert procRange() != 0


proc procProc: (proc (x: int): string) {.noInit.} =
  echo("proc: ", repr(result))  # => [Field0 = nil, Field1 = nil]

assert procProc() == nil

let statement

let
  x: array[0..2, int] = [0, 1, 2]
  y: int = 3

# static error: can not take the address
#echo(repr(x.addr))
#echo(repr(y.addr))

tuple unpacking

proc returnTuple: (int, int, int) =
  (0, 1, 2)

var
  (a, _, c) = returnTuple()
  d, e, f = returnTuple()

assert a == 0
assert c == 2
assert d == (0, 1, 2)
assert e == (0, 1, 2)
assert f == (0, 1, 2)

const section

static statement/expression

if statement

A new scope starts for the if/elif condition and ends after the corresponding then block:

if {| (let m = input =~ re"(\w+)=\w+"; m.isMatch):
  echo "key ", m[0], " value ", m[1]  |}
elif {| (let m = input =~ re""; m.isMatch):
  echo "new m in this scope" |}
else:
  # 'm' not declared here

In the example the scopes have been enclosed in {| |}.

case statement

let
  i = 1

case i:
  of 0 .. 2, 3 .. 4:
    echo("0 .. 2, 3 .. 4")
  of 5, 6:
    echo("5, 6")
  of 7 .. 9:
    echo("7 .. 9")
  else:
    discard


let
  r: range[0 .. 9] = 3

case r:
  of 0 .. 2, 3 .. 4:
    echo("0 .. 2, 3 .. 4")
  of 5, 6:
    echo("5, 6")
  of 7 .. 9:
    echo("7 .. 9")


const
  SymChars: set[char] = {'a' .. 'z', 'A' .. 'Z', '\x80' .. '\xff'}

var
  c = 'x'

case c:
  of SymChars, '_':
    echo("an identifier")
  of '0' .. '9':
    echo("a number")
  else:
    echo("other")


type
  Direction = enum
    north, south, east, west

var
  e = north

case e:
  of {Direction.low .. Direction(1)}:
    echo("north or south")
  of east .. west:
    echo("east or west")

when statement

when sizeof(int) == 2:
  echo("running on a 16 bit system!")
elif sizeof(int) == 4:
  echo("running on a 32 bit system!")
elif sizeof(int) == 8:
  echo("running on a 64 bit system!")
else:
  echo("cannot happen!")

return statement

yield statement

iterator itercount(start, last:int): int =
  var i = start
  while i <= last:
    yield i
    inc(i)

for i in itercount(0, 3):
  echo(i)

block statement

var
  found: bool = false
  arr: array[
    0 .. 2,
    array[0 .. 2, int]
  ] = [
    [0,1,2],
    [0,1,2],
    [0,1,7],
  ]
block myblock:
  for i in 0 .. 2:
    for j in 0 .. 2:
      if arr[j][i] == 7:
        found = true
        break myblock

assert found == true

break statement

var
  input: string

while true:
  input = readline(stdin)
  if input.len > 0:
    break

echo(input)

continue statement

while expr1:
  stmt1
  continue
  stmt2

Is equivalent to:

while expr1:
  block myBlockName:
    stmt1
    break myBlockName
    stmt2

if expression

var
  a = 7
  x = if a > 20: 20 elif a > 10: 10 else: 0
  y =
    if a > 20:
      20
    elif a > 10:
      10
    else:
      0
  z = if a > 20: 20
    elif a > 10: 10
    else: 0

assert x == 0
assert y == 0
assert z == 0

when expression

var
  x = when sizeof(int) == 2: "16bit" elif sizeof(int) == 4: "32bit" elif sizeof(int) == 8: "64bit" else: "unknown"
  y =
    when sizeof(int) == 2:
      "16bit"
    elif sizeof(int) == 4:
      "32bit"
    elif sizeof(int) == 8:
      "64bit"
    else:
      "unknown"
  z = when sizeof(int) == 2: "16bit"
    elif sizeof(int) == 4: "32bit"
    elif sizeof(int) == 8:"64bit"
    else: "unknown"

case expression

import strutils

var
  animal = "unknown"
  x =
    case animal
    of "dog": "bones"
    of "cat": "mice"
    elif animal.endsWith"whale": "plankton"
    else:
      echo "I'm not sure what to serve, but everybody loves ice cream"
      "ice cream"
  y =
    case animal
    of "dog":
      "bones"
    of "cat":
      "mice"
    elif animal.endsWith"whale":
      "plankton"
    else:
      echo "I'm not sure what to serve, but everybody loves ice cream"
      "ice cream"

The case expression can also introduce side effects. When multiple statements are given for a branch, Nim will use the last expression as the result value, much like in an expr template.

table constructor

  • The order of the (key,value)-pairs is preserved, thus it is easy to support ordered dicts with for example {key: val}.newOrderedTable.
  • A table literal can be put into a const section and the compiler can easily put it into the executable's data section just like it can for arrays and the generated data section requires a minimal amount of memory.
  • Every table implementation is treated equal syntactically.
  • Apart from the minimal syntactic sugar the language core does not need to know about tables.
const
  x = {"key1": "value1", "key2", "key3": "value2"}

assert x == [("key1", "value1"), ("key2", "value2"), ("key3", "value2")]


import tables

const
  s = x.newOrderedTable

type conversions

Syntactically a type conversion is like a procedure call, but a type name replaces the procedure name. A type conversion is always safe in the sense that a failure to convert a type to another results in an exception (if it cannot be determined statically).

Ordinary procs are often preferred over type conversions in Nim: For instance, $ is the toString operator by convention and toFloat and toInt can be used to convert from floating point to integer or vice versa.

type casts

cast[int](x)

Type casts are a crude mechanism to interpret the bit pattern of an expression as if it would be of another type. Type casts are only needed for low-level programming and are inherently unsafe.

addr operator

The addr operator returns the address of an l-value. If the type of the location is T, the addr operator result is of the type ptr T. An address is always an untraced reference. Taking the address of an object that resides on the stack is unsafe, as the pointer may live longer than the object on the stack and can thus reference a non-existing object. One can get the address of variables, but one can't use it on variables declared through let statements:

let s0 = "Hello"

var
  s1 = s0
  p0 : pointer = addr(s1)
  ptr0 : ptr = addr(s1)
  ptr1 : ptr string = addr(s1)

assert p0 == ptr0
assert ptr0 == ptr1


echo(repr(p0))
# --> 0x622c58

echo(repr(cast[ptr string](p0)))
# --> ref 0x622c58 --> 0x7f20a4a1d078"Hello"

echo(repr(ptr0))
# --> ref 0x622c58 --> 0x7f20a4a1d078"Hello"

echo(repr(ptr1))
# --> ref 0x622c58 --> 0x7f20a4a1d078"Hello"


echo repr(addr(s1))
# --> ref 0x622c58 --> 0x7f20a4a1d078"Hello"

echo cast[ptr string](p0)[]
# --> Hello


# The following line doesn't compile:
#echo repr(addr(s0))
# Error: expression has no address

procedure

Operators are procedures with a special operator symbol as identifier:

import strutils

proc `$` (x: int): string =
  # converts an integer to a string; this is a prefix operator.
  result = intToStr(x)

Operators with one parameter are prefix operators, operators with two parameters are infix operators. (However, the parser distinguishes these from the operator's position within an expression.) There is no way to declare postfix operators: all postfix operators are built-in and handled by the grammar explicitly.

Any operator can be called like an ordinary proc with the 'opr' notation. (Thus an operator can have more than two parameters):

proc `*+` (a, b, c: int): int =
  # Multiply and add
  result = a * b + c

assert `*+`(3, 4, 6) == `+`(`*`(3, 4), 6)

export marker

proc exportedEcho*(s: string) = echo s

proc `*`*(a: string; b: int): string =
  result = newStringOfCap(a.len * b)
  for i in 1..b:
    result.add a

assert "ABC" * 3 == "ABCABCABC"


var
  exportedVar*: int

const
  exportedConst* = 78

type
  ExportedType* = object
    exportedField*: int

method call syntax

import strutils

echo("abc".len)
# is the same as echo(len("abc"))

echo("abc".toUpper())

echo({'a', 'b', 'c'}.card)

stdout.writeln("Hallo")
# the same as writeln(stdout, "Hallo")

properties

type
  Socket* = ref object of RootObj
    FHost: int # cannot be accessed from the outside of the module
               # the `F` prefix is a convention to avoid clashes since
               # the accessors are named `host`

proc host*(s: Socket): int {.inline.} =
  ## getter of hostAddr
  s.FHost


proc `host=`*(s: var Socket, value: int) {.inline.} =
  ## setter of hostAddr
  s.FHost = value


var
  s: Socket

new(s)

s.host = 34
# same as `host=`(s, 34)

assert s.host == 34

closure

proc create_closure_seq(count: Positive): seq[proc: int] =
  newSeq(result, count)
  #for i in 0 .. count - 1:
  for i in countup(0, count - 1):
    result[i] = proc: int = int(i)


var
  result = (var a: array[0..2, int]; for i, p in create_closure_seq(3).pairs: a[i] = p(); a)

assert result == [2, 2, 2]

anonymous procedure

import algorithm


var
  cities = @["Frankfurt", "Tokyo", "New York"]

cities.sort(proc (x,y: string): int =
    cmp(x.len, y.len))


assert cities == @["Tokyo", "New York", "Frankfurt"]

nonoverloadable builtins

declared, defined, definedInScope, compiles, low, high, sizeOf, is, of, shallowCopy, getAst, astToStr, spawn, procCall

Thus they act more like keywords than like ordinary identifiers; unlike a keyword however, a redefinition may shadow the definition in the system module. From this list the following should not be written in dot notation x.f since x cannot be type checked before it gets passed to f:

declared, defined, definedInScope, compiles, getAst, astToStr

var parameters

proc divmod(a, b: int; res, remainder: var int) =
  res = a div b
  remainder = a mod b

var
  x, y: int

divmod(8, 5, x, y) # modifies x and y

assert x == 1
assert y == 3

The argument passed to a var parameter has to be an l-value. Var parameters are implemented as hidden pointers.
The above example is equivalent to:

proc divmod(a, b: int; res, remainder: ptr int) =
  res[] = a div b
  remainder[] = a mod b

var
  x, y: int

divmod(8, 5, addr(x), addr(y))

assert x == 1
assert y == 3

This can be done in a cleaner way by returning a tuple.

proc divmod(a, b: int): tuple[res, remainder: int] =
  result.res = a div b
  result.remainder = a mod b


var
  t = divmod(8, 5)

assert t.res == 1
assert t.remainder == 3


var
  (res, remainder) = divmod(8, 5)

assert res == 1
assert remainder == 3

Note: var parameters are never necessary for efficient parameter passing. Since non-var parameters cannot be modified the compiler is always free to pass arguments by reference if it considers it can speed up execution.

var return type

A proc, converter or iterator may return a var type which means that the returned value is an l-value and can be modified by the caller:

var g = 0

proc WriteAccessToG(): var int =
  result = g

WriteAccessToG() = 6

assert g == 6

It is a compile time error if the implicitly introduced pointer could be used to access a location beyond its lifetime:

# static error: address of 'g' may not escape its stack frame
proc WriteAccessToG(): var int =
  var g = 0
  result = g # Error!

For iterators, a component of a tuple return type can have a var type too:

var
  sq = @["a", "b", "c"]


# static error: 'str[0]' cannot be assigned to
#for i, str in sq.pairs:
#  str[0] = 'x'


for i, str in sq.mpairs:
  str[0] = 'x'


assert sq == @["x", "x", "x"]


# same iterator as built-in mpairs
iterator custom_mpairs(a: var seq[string]): tuple[key: int, val: var string] =
  for i in 0..a.high:
    yield (i, a[i])


for i, str in sq.custom_mpairs:
  str.add("y")


assert sq == @["xy", "xy", "xy"]


var
  arr: array[
    0..2, 
    array[0..2, int],
  ] = [
    [0, 1, 2],
    [0, 1, 2],
    [0, 1, 2],
  ]


# static error: 'item[0]' cannot be assigned to
#for item in arr.items:
#  item[0] = 2


for item in arr.mitems:
  item[0] = 2


assert arr[0] == [2, 1, 2]

In the standard library every name of a routine that returns a var type starts with the prefix m per convention.

Overloading of the subscript operator

The [] subscript operator for arrays/openarrays/sequences can be overloaded.

Multi-methods

Procedures always use static dispatch. Multi-methods use dynamic dispatch.

type
  Expression = ref object of RootObj ## abstract base class for an expression
  Literal = ref object of Expression
    x: int
  PlusExpr = ref object of Expression
    a, b: Expression


method eval(e: Expression): int =
  # override this base method
  quit "to override!"


method eval(e: Literal): int = return e.x


method eval(e: PlusExpr): int =
  # watch out: relies on dynamic binding
  result = eval(e.a) + eval(e.b)


proc newLit(x: int): Literal =
  new(result)
  result.x = x


proc newPlus(a, b: Expression): PlusExpr =
  new(result)
  result.a = a
  result.b = b


assert eval(
  newPlus(
    newPlus(
      newLit(1), newLit(2)
    ),
    newLit(4)
  )
) == 7
type
  Thing = ref object of RootObj
  Unit = ref object of Thing
    x: int


method collide(a, b: Thing): int {.inline.} =
  quit "to override!"


method collide(a: Thing, b: Unit): int {.inline.} =
  return 1


method collide(a: Unit, b: Thing): int {.inline.} =
  return 2


var a, b: Unit
new a
new b

assert collide(a, b) == 2

Invocation of a multi-method cannot be ambiguous: collide 2 is preferred over collide 1 because the resolution works from left to right. In the example (Unit, Thing) is preferred over (Thing, Unit).

Performance note: Nim does not produce a virtual method table, but generates dispatch trees. This avoids the expensive indirect branch for method calls and enables inlining. However, other optimizations like compile time evaluation or dead code elimination do not work with methods.

import math

type
  Dollar* = distinct float

  Kg* = distinct float

  Fruit* = ref object {.inheritable.}
    origin*: string
    price*: Dollar

  Banana* = ref object of Fruit
    size*: int

  Pumpkin* = ref object of Fruit
    weight*: Kg

  BigPumpkin* = ref object of Pumpkin

  Bucket* {.inheritable.} = ref object
    fruits*: seq[string]


# op proc
proc `$` *(x: Dollar): string {.borrow.}

proc `$` *(x: Kg): string {.borrow.}

proc `==` *(x, y: Dollar): bool {.borrow, inline.}


# op method
method `$`*(self: Fruit): string =
  "Fruit: origin='" & self.origin & ", price=" & $self.price

method `$`*(self: Banana): string =
  result = procCall($Fruit(self)) & ", size=" & $self.size
  result[0 .. 4] = "Banana"

method `$`*(self: Pumpkin): string =
  result = procCall($Fruit(self)) & ", weight=" & $self.weight
  result[0 .. 4] = "Pumpkin"

method `$`*(self: BigPumpkin): string =
  result = procCall($Fruit(self)) & ", weight=" & $self.weight
  result[0 .. 4] = "BigPumpkin"


# reduction method
method reduction_m(self: Fruit): Dollar =
  echo(
    "Called Fruit.reduction_m (" & $self & ")"
  )
  Dollar(0)

method reduction_m(self: Banana): Dollar =
  echo(
    "Called Banana.reduction_m (" & $self & ")"
  )
  Dollar(9)

method reduction_m(self: Pumpkin): Dollar =
  echo(
    "Called Pumpkin.reduction_m (" & $self & ")"
  )
  Dollar(1)


# reduction proc
proc reduction_p(self: Fruit): Dollar =
  echo(
    "Called Fruit.reduction_p (" & $self & ")"
  )
  Dollar(0)

proc reduction_p(self: Banana): Dollar =
  echo(
    "Called Banana.reduction_p (" & $self & ")"
  )
  Dollar(9)

proc reduction_p(self: Pumpkin): Dollar =
  echo(
    "Called Pumpkin.reduction_p (" & $self & ")"
  )
  Dollar(1)



# calcPrice method
method calcPrice_m*(self: Fruit): Dollar =
  echo(
    "Called Fruit.calcPrice_m (" & $self & ")"
  )
  Dollar(round(float(self.price) * 100) / 100 - float(self.reduction_m))

method calcPrice_m*(self: Banana): Dollar =
  echo(
    "Called Banana.calcPrice_m (" & $self & ")"
  )
  procCall Fruit(self).calcPrice_m()

method calcPrice_m*(self: Pumpkin): Dollar =
  echo(
    "Called Pumpkin.calcPrice_m (" & $self & ")"
  )
  Dollar(float(procCall(Fruit(self).calcPrice_m())) * float(self.weight))

method calcPrice_m*(self: Bigpumpkin): Dollar =
  echo(
    "Called Bigpumpkin.calcPrice_m (" & $self & ")"
  )
  Dollar(1000)


# calcPrice proc
proc calcPrice_p*(self: Fruit): Dollar =
  echo(
    "Called Fruit.calcPrice_p (" & $self & ")"
  )
  Dollar(round(float(self.price) * 100) / 100 - float(self.reduction_p))

proc calcPrice_p*(self: Banana): Dollar =
  echo(
    "Called Banana.calcPrice_p (" & $self & ")"
  )
  calcPrice_p(Fruit(self))

proc calcPrice_p*(self: Pumpkin): Dollar =
  echo(
    "Called Pumpkin.calcPrice_p (" & $self & ")"
  )
  Dollar(float(calcPrice_p(Fruit(self))) * float(self.weight))

proc calcPrice_p*(self: Bigpumpkin): Dollar =
  echo(
    "Called Bigpumpkin.calcPrice_p (" & $self & ")"
  )
  Dollar(1000)


# add method
method add(self: Bucket, fruit: Fruit) =
  echo(
    "Called Bucket.Add (" & $fruit & ")"
  )
  self.fruits.add($fruit)

method add(self: Bucket, fruit: Banana) =
  echo(
    "Called Bucket.Add (" & $fruit & ")"
  )
  procCall self.add(Fruit(fruit))

method add(self: Bucket, fruit: Pumpkin) =
  echo(
    "Called Bucket.add (" & $fruit & ")"
  )
  procCall self.add(Fruit(fruit))


# Constructor
proc newBanana*(size: int, origin: string, price: float): Banana =
  new(result)
  result.origin = origin
  result.price = Dollar(price)
  result.size = size

proc newPumpkin*(weight: float, origin: string, price: float): Pumpkin =
  new(result)
  result.origin = origin
  result.price = Dollar(price)
  result.weight = Kg(weight)

proc newBigPumpkin*(weight: float, origin: string, price: float): BigPumpkin =
  new(result)
  result.origin = origin
  result.price = Dollar(price)
  result.weight = Kg(weight)

proc newBucket(): Bucket =
  new(result)
  result.fruits = newSeq[string]()


if isMainModule:
  var banana = newBanana(size=10, origin="country_0", price=1000)
  var pumpkin = newPumpkin(weight=100, origin="country_1", price=10000)
  var big_pumpkin = newBigPumpkin(weight=1000, origin="country_1", price=20000)

  assert banana.calcPrice_m() == 991.0.Dollar
  # --> Called Banana.calcPrice_m (Banana: origin='country_0, price=1000.0, size=10)
  # --> Called Fruit.calcPrice_m (Banana: origin='country_0, price=1000.0, size=10)
  # --> Called Banana.reduction_m (Banana: origin='country_0, price=1000.0, size=10)

  assert banana.calcPrice_p() == 1000.0.Dollar
  # --> Called Banana.calcPrice_p (Banana: origin='country_0, price=1000.0, size=10)
  # --> Called Fruit.calcPrice_p (Banana: origin='country_0, price=1000.0, size=10)
  # --> Called Fruit.reduction_p (Banana: origin='country_0, price=1000.0, size=10)

  assert pumpkin.calcPrice_m() == 999900.0.Dollar
  # --> Called Pumpkin.calcPrice_m (Pumpkin: origin='country_1, price=10000.0, weight=100.0)
  # --> Called Fruit.calcPrice_m (Pumpkin: origin='country_1, price=10000.0, weight=100.0)
  # --> Called Pumpkin.reduction_m (Pumpkin: origin='country_1, price=10000.0, weight=100.0)

  assert pumpkin.calcPrice_p() == 1000000.0.Dollar
  # --> Called Pumpkin.calcPrice_p (Pumpkin: origin='country_1, price=10000.0, weight=100.0)
  # --> Called Fruit.calcPrice_p (Pumpkin: origin='country_1, price=10000.0, weight=100.0)
  # --> Called Fruit.reduction_p (Pumpkin: origin='country_1, price=10000.0, weight=100.0)

  var bucket = newBucket()
  bucket.add(banana)
  # --> Called Bucket.Add (Banana: origin='country_0, price=1000.0, size=10)
  # --> Called Bucket.Add (Banana: origin='country_0, price=1000.0, size=10)

  bucket.add(pumpkin)
  # --> Called Bucket.add (Pumpkin: origin='country_1, price=10000.0, weight=100.0)
  # --> Called Bucket.Add (Pumpkin: origin='country_1, price=10000.0, weight=100.0)
  bucket.add(big_pumpkin)
  # --> Called Bucket.add (BigPumpkin: origin='country_1, price=20000.0, weight=1000.0)
  # --> Called Bucket.Add (BigPumpkin: origin='country_1, price=20000.0, weight=1000.0)
  echo($bucket.fruits)
  # --> @[Banana: origin='country_0, price=1000.0, size=10, Pumpkin: origin='country_1, price=10000.0, weight=100.0, BigPumpkin: origin='country_1, price=20000.0, weight=1000.0]

iterators and for statement

iterator triples *(s: string): (Natural, int, char) {.inline.} =
  var i = 0
  while i < len(s):
    yield (i.Natural, ord(s[i]), s[i])
    inc(i)


var q = newSeq[(int, char)](2)

for i, o, c in "hi".triples:
  q[i] = (o, c)

assert q == @[(104, 'h'), (105, 'i')]

implicit items/pairs invocation

first class iterators

  • inline iterator
    Inline iterators are second class citizens; They can be passed as parameters only to other inlining code facilities like templates, macros and other inline iterators.
  • closure iterator
    Can be passed around more freely.

restrictions:

  • yield in a closure iterator can not occur in a try statement.
  • For now, a closure iterator cannot be evaluated at compile time.
  • return is allowed in a closure iterator (but rarely useful).
  • Both inline and closure iterators cannot be recursive.

Iterators that are neither marked {.closure.} nor {.inline.} explicitly default to being inline, but that this may change in future versions of the implementation.

The iterator type is always of the calling convention closure implicitly; the following example shows how to use iterators to implement a collaborative tasking system:

iterator count0(): int {.closure.} =
  yield 0


iterator count2(): int {.closure.} =
  var x = 1
  yield x
  inc x
  yield x


proc invoke(iter: iterator(): int {.closure.}): seq[int] =
  newSeq(result, 0)
  for x in iter():
    result.add(x)


assert invoke(count0) == @[0]
assert invoke(count2) == @[1, 2]
import sequtils

type
  Task = iterator (ticker: int)

iterator a1(ticker: int) {.closure.} =
  echo "a1: A"
  yield
  echo "a1: B"
  yield
  echo "a1: C"

iterator a2(ticker: int) {.closure.} =
  echo "a2: A"
  yield
  echo "a2: B"
  yield
  echo "a2: C"
  yield
  echo "a2: D"
  yield
  echo "a2: E"
  yield
  echo "a2: F"

iterator a3(ticker: int) {.closure.} =
  echo "a3: A"
  yield
  echo "a3: B"
  yield
  echo "a3: C"


proc runTasks(tasks: varargs[Task]) =
  var
    task_seq: seq[Task] = toSeq(tasks.items)
    ticker = 0

  while true:
    let
      x = task_seq[ticker]

    x(ticker)

    if finished(x):
      task_seq.delete(ticker)
      if task_seq.len == 0:
        break
      elif ticker >= task_seq.high:
        ticker = 0
    else:
      ticker = (ticker + 1) mod task_seq.len


runTasks(a1, a2, a3)

The builtin system.finished can be used to determine if an iterator has finished its operation; no exception is raised on an attempt to invoke an iterator that has already finished its work.

Note that system.finished is error prone to use because it only returns true one iteration after the iterator has finished:

iterator mycount(a, b: int): int {.closure.} =
  var x = a
  while x <= b:
    yield x
    inc x

var
  c = mycount # instantiate the iterator
  result: seq[int] = @[]

while not finished(c):
  result.add(c(1, 3))


assert result == @[1, 2, 3, 0]
# The last '0' is not intended value.

Instead this code has be used;

iterator mycount(a, b: int): int {.closure.} =
  var x = a
  while x <= b:
    yield x
    inc x

var
  c = mycount # instantiate the iterator
  result: seq[int] = @[]

while true:
  let value = c(1, 3)
  if finished(c):
    break # and discard 'value'!
  result.add(value)


assert result == @[1, 2, 3]

It helps to think that the iterator actually returns a pair (value, done) and finished is used to access the hidden done field.

Closure iterators are resumable functions and so one has to provide the arguments to every call. To get around this limitation one can capture parameters of an outer factory proc:

proc mycount(a, b: int): iterator: (int, int) =
  result = iterator: (int, int) =
    var x = a
    while x <= b:
      yield (x, x)
      inc(x)

var
  result: seq[int] = @[]
let
  c = mycount(1, 3)


for a, b in c():
  result.add(a)

assert result == @[1, 2, 3]

converters

Type sections

type # example demonstrating mutually recursive types
  Node = ref NodeObj # a traced pointer to a NodeObj
  NodeObj = object
    le, ri: Node     # left and right subtrees
    sym: ref Sym     # leaves contain a reference to a Sym
  
  Sym = object       # a symbol
    name: string     # the symbol's name
    line: int        # the line the symbol was declared in
    code: Node      # the symbol's abstract syntax tree


# http://forum.nim-lang.org/t/1422
type
  Obj = object
    x: int
    y: int

  Ref = ref Obj

  RefOrObj = Ref or Obj


proc init(arg: var RefOrObj, x, y: int) =
  when arg is ref:
    new(arg)
  arg.x = x
  arg.y = y


proc sum(arg: RefOrObj): int =
  arg.x + arg.y


proc main() =
  var
    o: Obj
    r: Ref

  init(o, 1, 1)
  assert sum(o) == 2

  init(r, 2, 2)
  assert sum(r) == 4

main()

A type section begins with the type keyword. It contains multiple type definitions. A type definition binds a type to a name. Type definitions can be recursive or even mutually recursive. Mutually recursive types are only possible within a single type section. Nominal types like objects or enums can only be defined in a type section.

Exception handling

try statement

import strutils

var
  f: File

if open(f, "/root/.bashrc"):
  try:
    var a = readLine(f)
    var b = readLine(f)
    echo("sum: " & $(parseInt(a) + parseInt(b)))
  except Exception:
    echo("Catch all !")
    let
      exc = getCurrentException()
      desc = ": parent=" & repr(exc.parent) & ", msg=" & exc.msg
 
    if exc of ValueError:
      # could not convert string to integer
      echo("could not convert string to integer. ValueError", desc)
    elif exc of OverflowError:
      echo("overflow!. OverflowError", desc)
    elif exc of IOError:
      echo("IO error!. IOError", desc)
    else:
      echo("Unknown exception!: Exception", desc)

  # no-op
  except OverflowError:
    echo("overflow!")
  except ValueError:
    echo("could not convert string to integer")
  except IOError:
    echo("IO error!")
  except:
    echo("Unknown exception!")
  finally:
    close(f)

Try expression

import strutils

let
  x = try: parseInt("133a")
    except: -1
    finally: echo "parseInt finished"

assert x == -1


let
  y =
    try:
      parseint("133a")
    except:
       -1
    finally:
      echo "parseInt finished"

assert y == -1

To prevent confusing code there is a parsing limitation; if the try follows a ( it has to be written as a one liner

import strutils

let
  x = (try: parseInt("133a") except: -1 finally: echo "parseInt finished")


assert x == -1

Except clauses

Note that getCurrentException always returns a ref Exception type. If a variable of the proper type is needed (in the example above, IOError), one must convert it explicitly:

import strutils

var
  x: int


try:
  x = parseInt("133a")
except ValueError:
  let
    ref_exc = getCurrentException()
    exc = (ref ValueError)(getCurrentException())
  echo(repr(ref_exc))
  echo(repr(exc))
  assert ref_exc == exc


try:
  x = parseInt("133a")
except ValueError:
  echo("ValueError: ", getCurrentExceptionMsg())

Defer statement

Instead of a try finally statement a defer statement can be used.

Any statements following the defer in the current block will be considered to be in an implicit try block:
Top level defer statements are not supported since it's unclear what such a statement should refer to.

import streams


proc withDefer() =
  var
    f: File

  if f.open(filename="/tmp/numbers.txt", mode=FileMode.fmWrite):
    defer:
      close(f)

    f.write("abc")
    f.write("abc")
    f.write("\n")

withDefer()


proc sameAsDefer() =
  var
    f: File

  if f.open(filename="/tmp/numbers.txt", mode=FileMode.fmWrite):
    try:
      f.write("abc")
      f.write("def")
      f.write("\n")
    finally:
      f.close()

sameAsDefer()

Raise statement

The raise statement is the only way to raise an exception.

If no exception name is given, the current exception is re-raised. The ReraiseError exception is raised if there is no exception to re-raise. It follows that the raise statement always raises an exception (unless a raise hook has been provided).

raise newException(OSError, "OS error")

Effect system

Exception tracking

The raises pragma can be used to explicitly define which exceptions a proc/iterator/method/converter is allowed to raise. The compiler verifies this.

An empty raises list (raises: []) means that no exception may be raised.

proc p(what: bool) {.raises: [IOError, OSError].} =
  if what:
    raise newException(IOError, "IO")
  else:
    raise newException(OSError, "OS")


proc p(): bool {.raises: [].} =
  try:
    result = true
  except:
    result = false

A raises list can also be attached to a proc type. This affects type compatibility:

type
  Callback = proc (s: string) {.
    raises: [IOError],
  .}


var
  c: Callback


proc p(x: string) =
  raise newException(OSError, "OS")


# static error: type error
c = p

For a routine p the compiler uses inference rules to determine the set of possibly raised exceptions; the algorithm operates on p's call graph:

  1. Every indirect call via some proc type T is assumed to raise system.Exception (the base type of the exception hierarchy) and thus any exception unless T has an explicit raises list. However if the call is of the form f(...) where f is a parameter of the currently analysed routine it is ignored. The call is optimistically assumed to have no effect. Rule 2 compensates for this case.
  2. Every expression of some proc type wihtin a call that is not a call itself (and not nil) is assumed to be called indirectly somehow and thus its raises list is added to p's raises list.
  3. Every call to a proc q which has an unknown body (due to a forward declaration or an importc pragma) is assumed to raise system.Exception unless q has an explicit raises list.
  4. Every call to a method m is assumed to raise system.Exception unless m has an explicit raises list.
  5. For every other call the analysis can determine an exact raises list.
  6. For determining a raises list, the raise and try statements of p are taken into consideration.

Rules 1-2 ensure the following works:

proc noRaise(x: proc()) {.raises: [].} =
  # unknown call that might raise anything, but valid:
  x()

proc doRaise() {.raises: [IOError].} =
  raise newException(IOError, "IO")

proc use() {.raises: [].} =
  # doesn't compile! Can raise IOError!
  noRaise(doRaise)

So in many cases a callback does not cause the compiler to be overly conservative in its effect analysis.

Tag tracking

The exception tracking is part of Nim's effect system. Raising an exception is an effect. Other effects can also be defined. A user defined effect is a means to tag a routine and to perform checks against this tag:

type
  IO = object ## input/output effect


proc readLine(): string {.tags: [IO].} =
  "some line"


proc IO_please() {.tags: [IO].} =
  let
    x = readLine()


proc no_IO_please() {.tags: [].} =
  # the compiler prevents this:
  let
    x = readLine()

A tag has to be a type name. A tags list - like a raises list - can also be attached to a proc type. This affects type compatibility.

The inference for tag tracking is analogous to the inference for exception tracking.

Read/Write tracking

Note: Read/write tracking is not yet implemented!

The inference for read/write tracking is analogous to the inference for exception tracking.

Effects pragma

The effects pragma has been designed to assist the programmer with the effects analysis. It is a statement that makes the compiler output all inferred effects up to the effects's position:

proc proc0(what: bool) =
  if what:
    raise newException(IOError, "IO")
    {.effects.}
    # --> Hint: ref IOError [User]
  else:
    raise newException(OSError, "OS")


proc proc1(what: bool) =
  if what:
    raise newException(IOError, "IO")
  else:
    raise newException(OSError, "OS")
    {.effects.}
    # --> Hint: ref IOError [User]
    # --> Hint: ref OSError [User]

The compiler produces a hint message that IOError can be raised. OSError is not listed as it cannot be raised in the branch the effects pragma appears in.

Generics

type
  BinaryTreeObj[T] = object
    ## BinaryTreeObj is a generic type with generic param ``T``
    left, right: BinaryTree[T]
    ## left and right subtrees; may be nil
    data: T
    ## the data stored in a node

  BinaryTree[T] = ref BinaryTreeObj[T]
  ## a shorthand for notational convenience


proc newNode[T](data: T): BinaryTree[T] =
  ## constructor for a node
  new(result)
  result.data = data


proc add[T](
  root: var BinaryTree[T],
  n: BinaryTree[T],
) =
  if root == nil:
    root = n
  else:
    var
      it = root
    while it != nil:
      var
        c = cmp(it.data, n.data)
        ## compare the data items;
        ## uses the generic ``cmp`` proc that works for any type that
        ## has a ``==`` and ``<`` operator

      if c < 0:
        if it.left == nil:
          it.left = n
          return
        it = it.left
      else:
        if it.right == nil:
          it.right = n
          return
        it = it.right


iterator inorder[T](root: BinaryTree[T]): T =
  ## inorder traversal of a binary tree
  ## recursive iterators are not yet implemented, so this does not
  ## work in the current compiler!
  if root.left != nil:
    yield inorder(root.left)
  if root.right != nil:
    yield inorder(root.right)


var
  root: BinaryTree[string]
  # instantiates a BinaryTree with the type string


root.add(newNode("hallo"))
root.add(newNode("world"))
# instantiates generic procs ``newNode`` and ``add``


for str in inorder(root):
  echo(str)

is operator

type
  Table[Key, Val] = object
    keys: seq[Key]
    values: seq[Val]
    count: Natural
    when not (Key is string):  # nil value for strings used for optimization
      deletedKeys: seq[bool]


proc initTable*[Key, Val](initialSize: Natural = 64): Table[Key, Val] =
  result.count = 0
  newSeq(result.keys, initialSize)
  newSeq(result.values, initialSize)
  echo("String init")
  #newSeq(result.deletedKeys, 0)


proc add*[Key, Val](t: var Table[Key, Val], key: Key, value: Val) =
  t.keys[t.count] = key
  t.values[t.count] = value
  t.count += 1


var
  t: Table[string, int] = initTable[string, int]()

t.add("A", 1)

type operator

var
  x = 0

var
  y: type(x)

If type is used to determine the result type of a proc/iterator/converter call c(X) (where X stands for a possibly empty list of arguments), the interpretation where c is an iterator is preferred over the other interpretations:

import strutils

# strutils contains some ``split`` procs and iterators.
#   proc split(s: string; seps: set[char] = Whitespace): seq[string]
#   proc split(s: string; sep: char): seq[string]
#   proc split(s: string; sep: string): seq[string]
#   iterator split(s: string; seps: set[char] = Whitespace): string
#   iterator split(s: string; sep: char): string
#   iterator split(s: string; sep: string): string
# But since an iterator is the preferred interpretation, `y' has the
# type ``string``:

var
  y: type("a b c".split)


y = "a"

Type classes

A type class is a special pseudo-type that can be used to match against types in the context of overload resolution or the is operator. Nim supports the following built-in type classes:

type class matches
object any object type
tuple any tuple type
enum any enumeration
proc any proc type
ref any ref type
ptr any ptr type
var any var type
distinct any distinct type
array any array type
set any set type
seq any seq type
auto any type

Every generic type automatically creates a type class of the same name that will match any instantiation of the generic type.

Type classes can be combined using the standard boolean operators to form more complex type classes:

# create a type class that will match all tuple and object types
import tables

type
  ObjX = object
    x: int
    y: int
    z: int

  RefX = ref ObjX

  RecordType = tuple[x:int, y:int, z:int] or object


proc printFields(rec: RecordType) =
  var
    t = initTable[string, int]()
  for k, v in fieldPairs(rec):
    t[k] = v
  assert t == {"x": 1, "y": 2, "z": 3}.toTable

var
  tuple_rec: tuple[x:int, y:int, z:int] = (x: 1, y: 2, z: 3)
  obj_rec = ObjX(x: 1, y: 2, z: 3)

printFields(tuple_rec)
printFields(obj_rec)

Procedures utilizing type classes in such manner are considered to be implicitly generic. They will be instantiated once for each unique combination of param types used within the program.

Nim also allows for type classes and regular types to be specified as type constraints of the generic type parameter:

proc gproc[T: int|string](x, y: T): string =
  "accepted by A"

assert gproc(100, 200) == "accepted by A"
assert gproc("a", "b") == "accepted by A"

# static error: type mismatch
#assert gproc(100, "b") == "accepted by A"
#assert gproc("a", 200) == "accepted by A"


proc gproc[X: int|string, Y: string](x:X, y:Y): string =
  "accepted by B"


assert gproc(100, 200) == "accepted by A"

# static error: ambiguous call
#assert gproc("a", "b") == "accepted by A"

assert gproc(100, "b") == "accepted by B"

# static error: type mismatch
assert gproc("a", 200) == "accepted by A"

By default, during overload resolution each named type class will bind to exactly one concrete type. Here is an example taken directly from the system module to illustrate this:

proc `==`*[T: tuple|object](x, y: T): bool =
  ## generic ``==`` operator for tuples that is lifted from the components
  ## of `x` and `y`.
  for a, b in fields(x, y):
    if a != b: return false
  return true

Alternatively, the distinct type modifier can be applied to the type class to allow each param matching the type class to bind to a different type.

If a proc param doesn't have a type specified, Nim will use the distinct auto type class (also known as any):

proc concat(a, b): string = $a & $b

assert concat("str+", 1) == "str+1"

Procs written with the implicitly generic style will often need to refer to the type parameters of the matched generic type. They can be easily accessed using the dot syntax:

import typetraits

type
  NameEnum = enum
    first_name,
    last_name,
    middle_name

type
  NormalMatrix[Rows, Cols, T] = array[Rows, array[Cols, T]]

  Normal3Matrix[T] = NormalMatrix[range[0..2], range[0..2], T]

  NameEnumMatrix[T] = NormalMatrix[range[0..2], NameEnum, T]


proc `*`* [Rows, Cols, Ta, Tb](
  a: NormalMatrix[Rows, Cols, Ta],
  b: NormalMatrix[Rows, Cols, Tb],
): NormalMatrix[Rows, Cols, Ta] =
  for r in Rows.low .. Rows.high:
    var
      inner: array[Cols, int]
    for c in Cols.low .. Cols.high:
      inner[c] = a[r][c] * b[r][c]
    result[r] = inner

var
  normalm: NormalMatrix[range[0..2], range[0..2], int] = [
    [0, 1, 2],
    [10, 11, 12],
    [20, 21, 22],
  ]
  normal3m: Normal3Matrix[int] = [
    [0, 1, 2],
    [10, 11, 12],
    [20, 21, 22],
  ]
  enumm: NameEnumMatrix[string] = [
    ["Fn0", "Ln0", "Mn0"],
    ["Fn1", "Ln1", "Mn1"],
    ["Fn2", "Ln2", "Mn2"],
  ]

assert normalm.type.name == "NormalMatrix[range 0..2(int), range 0..2(int), system.int]"
assert normalm[2][2] == 22
assert normalm.Rows is range[0..2]
assert normalm.Cols is range[0..2]
assert normalm.T is int
assert normal3m.type.name == "Normal3Matrix[system.int]"
assert normal3m[2][2] == 22
assert normal3m.Rows is range[0..2]
assert normal3m.Cols is range[0..2]
assert normal3m.T is int
assert enumm.type.name == "NameEnumMatrix[system.string]"
assert enumm[2][NameEnum.middle_name] == "Mn2"
assert enumm[2][NameEnum(2)] == "Mn2"
assert enumm.Rows is range[0..2]
assert enumm.Cols is NameEnum
assert enumm.T is string
assert normalm * normal3m == [[0, 1, 4], [100, 121, 144], [400, 441, 484]]


type
  StaticMatrix[Rows, Cols: static[int], T] = array[Rows, array[Cols, T]]
  Static3Matrix[T] = StaticMatrix[3, 3, T]

var
  staticm: StaticMatrix[3, 3, int] = [
    [0, 1, 2],
    [10, 11, 12],
    [20, 21, 22],
  ]
  static3m: Static3Matrix[int] = [
    [0, 1, 2],
    [10, 11, 12],
    [20, 21, 22],
  ]

assert staticm.type.name == "StaticMatrix[3, 3, system.int]"
assert staticm.Rows == 3
assert staticm.Cols == 3
assert staticm.T is int
assert static3m.type.name == "Static3Matrix[system.int]"
assert static3m.Rows == 3
assert static3m.Cols == 3
assert static3m.T is int
assert staticm * static3m == [[0, 1, 4], [100, 121, 144], [400, 441, 484]]



type
  #LwMatrix[Rows, Cols: static[range], T] = array[(high(Rows) + 1) * (high(Cols) + 1), T]
  StaticLwMatrix[Rows, Cols: static[int], T] = array[Rows * Cols, T]
  StaticLw3Matrix[T] = StaticLwMatrix[3, 3, T]

proc `[]`* (a: StaticLwMatrix, row, col: int): StaticLwMatrix.T =
  if (row >= 0 and row >= StaticLwMatrix.Rows) or
    (row < 0 and row < -StaticLwMatrix.Rows) or
    (col >=0 and col >= StaticLwMatrix.Cols) or
    (col < 0 and col < -StaticLwMatrix.Cols):
      raise newException(IndexError, "index out of bounds")
  a[StaticLwMatrix.Cols * row + (if col >= 0: col else: StaticLwMatrix.Cols + col)]


# same as above
proc `[]`* [Rows, Cols, T](a: StaticLwMatrix[Rows, Cols, T], row, col: T): T =
  if (row >= 0 and row >= Rows) or
    (row < 0 and row < -Rows) or
    (col >=0 and col >= Cols) or
    (col < 0 and col < -Cols):
      raise newException(IndexError, "index out of bounds")
  a[Cols * row + (if col >= 0: col else: Cols + col)]


var
  staticlwm: StaticLwMatrix[3, 3, int] = [
    0, 1, 2,
    10, 11, 12,
    20, 21, 22,
  ]
  staticlw3m: StaticLw3Matrix[int] = [
    0, 1, 2,
    10, 11, 12,
    20, 21, 22,
  ]

assert staticlwm.type.name == "StaticLwMatrix[3, 3, system.int]"
assert staticlwm.Rows == 3
assert staticlwm.Cols == 3
assert staticlwm.T is int
assert staticlw3m.type.name == "StaticLw3Matrix[system.int]"
assert staticlw3m.Rows == 3
assert staticlw3m.Cols == 3
assert staticlw3m.T is int
assert staticlw3m[1, -1] == 12


type
  NormalMatrixObj[Rows, Cols, T] = object
    data: array[Rows, array[Cols, T]]

  NameEnumMatrixObj[Rows, T] = object
    data: array[Rows, array[NameEnum, T]]

  StaticMatrixObj[Rows, Cols: static[int], T] = object
    data: array[Rows, array[Cols, T]]

var
  normalm_obj = NormalMatrixObj[range[0..2], range[0..2], int](
    data: [
      [0, 1, 2],
      [10, 11, 12],
      [20, 21, 22],
    ]
  )
  enumm_obj = NameEnumMatrixObj[range[0..2], string](
    data: [
      ["Fn0", "Ln0", "Mn0"],
      ["Fn1", "Ln1", "Mn1"],
      ["Fn2", "Ln2", "Mn2"],
    ]
  )
  staticm_obj = StaticMatrixObj[3, 3, int](
    data: [
      [0, 1, 2],
      [10, 11, 12],
      [20, 21, 22],
    ]
  )


assert normalm_obj.type.name == "NormalMatrixObj[range 0..2(int), range 0..2(int), system.int]"
assert enumm_obj.type.name == "NameEnumMatrixObj[range 0..2(int), system.string]"
assert staticm_obj.type.name == "StaticMatrixObj[3, 3, system.int]"

Alternatively, the type operator can be used over the proc params for similar effect when anonymous or distinct type classes are used.

When a generic type is instantiated with a type class instead of a concrete type, this results in another more specific type class:

16
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?