Nimrod logo

The ultimate introduction









Slides

git clone https://github.com/Araq/oscon2015



Download

http://nim-lang.org/download.html

Installation

git clone -b devel git://github.com/nim-lang/Nim.git
cd Nim
git clone -b devel --depth 1 git://github.com/nim-lang/csources
cd csources && sh build.sh
cd ..
bin/nim c koch
./koch boot -d:release

What is Nim?

Philosophy

Why Nim?

Uses of Nim

Nim at 3dicc

URLs

Websitehttp://nim-lang.org
Mailing listhttp://www.freelists.org/list/nim-dev
Forumhttp://forum.nim-lang.org
Githubhttps://github.com/Araq/Nim
IRCirc.freenode.net/nim

Hello World

echo "hello world!"

Hello World

echo "hello world!"
nim c -r hello.nim

More Code!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
proc decimalToRoman*(number: range[1..3_999]): string =
  ## Converts a number to a Roman numeral.
  const romanComposites = {
    "M": 1000, "CM": 900,
    "D": 500, "CD": 400, "C": 100,
    "XC": 90, "L": 50, "XL": 40, "X": 10, "IX": 9,
    "V": 5, "IV": 4, "I": 1}
  result = ""
  var decVal = number.int
  for key, val in items(romanComposites):
    while decVal >= val:
      decVal -= val
      result.add(key)

echo decimalToRoman(1009) # MIX

Nimble

Function application

Function application is f(), f(a), f(a, b).

Function application

Function application is f(), f(a), f(a, b).

And here is the sugar:

SugarMeaningExample
f af(a)spawn log("some message")
a.f()f(a)db.fetchRow()
a.ff(a)mystring.len
f a, bf(a, b)echo "hello ", "world"
a.f(b)f(a, b)myarray.map(f)
a.f bf(a, b)db.fetchRow 1
f"\n"f(r"\n")re"\b[a-z*]\b"

Function application

Function application is f(), f(a), f(a, b).

And here is the sugar:

SugarMeaningExample
f af(a)spawn log("some message")
a.f()f(a)db.fetchRow()
a.ff(a)mystring.len
f a, bf(a, b)echo "hello ", "world"
a.f(b)f(a, b)myarray.map(f)
a.f bf(a, b)db.fetchRow 1
f"\n"f(r"\n")re"\b[a-z*]\b"

BUT: f does not mean f(); myarray.map(f) passes f to map

Operators

Operators

proc `++`(x: var int; y: int = 1; z: int = 0) =
  x = x + y + z

var g = 70
++g
g ++ 7
g.`++`(10, 20)
echo g  # writes 108

Control flow

If vs when

1
2
3
4
5
6
7
when defined(posix):
  proc getCreationTime(file: string): Time =
    var res: Stat
    if stat(file, res) < 0'i32:
      let error = osLastError()
      raiseOSError(error)
    return res.st_ctime

Statements vs expressions

Statements require indentation:

# no indentation needed for single assignment statement:
if x: x = false

# indentation needed for nested if statement:
if x:
  if y:
    y = false
else:
  y = true

# indentation needed, because two statements follow the condition:
if x:
  x = false
  y = false

Statements vs expressions

Expressions do not:

if thisIsaLongCondition() and
    thisIsAnotherLongCondition(1,
       2, 3, 4):
  x = true

Builtin types

Builtin types

Builtin types

Builtin types

Builtin types

tuple

1
2
3
4
5
6
7
8
9
proc `+-`(x, y: int): (int, int) = (x - y, x + y)
# alternatively
proc `+-`(x, y: int): tuple[lowerBound, upperBound: int] = (x - y, x + y)

let tup = 100 +- 10
echo tup[0], " ", tup.upperBound

# tuple unpacking
let (lower, _) = 100 +- 10

Builtin types

object

1
2
3
4
5
6
7
8
9
type
  Rect = object
    x, y, w, h: int

# construction:
let r = Rect(x: 12, y: 22, w: 40, h: 80)

# field access:
echo r.x, " ", r.y

Builtin types

enums & sets

1
2
3
4
5
6
7
8
9
type
  SandboxFlag* = enum        ## what the interpreter should allow
    allowCast,               ## allow unsafe language feature: 'cast'
    allowFFI,                ## allow the FFI
    allowInfiniteLoops       ## allow endless loops
  SandboxFlags* = set[SandboxFlag]

proc runNimCode(code: string; flags: SandboxFlags = {allowCast, allowFFI}) =
  ...

Builtin types

1
2
3
4
5
6
7
#define allowCast (1 << 0)
#define allowFFI (1 << 1)
#define allowInfiniteLoops (1 << 1)

void runNimCode(char* code, unsigned int flags = allowCast|allowFFI);

runNimCode("4+5", 700);

Builtin types

ref and ptr

Regular expressions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Model a regular expression
type
  RegexKind = enum          ## the regex AST's kind
    reChar,                 ## character node  "c"
    reCClass,               ## character class node   "[a-z]"
    reStar,                 ## star node   "r*"
    rePlus,                 ## plus node   "r+"
    reOpt,                  ## option node  "r?"
    reCat,                  ## concatenation node "ab"
    reAlt,                  ## alternatives node "a|b"
    reWordBoundary          ## "\b"
  
  RegExpr = ref object
    case kind: RegexKind
    of reWordBoundary: discard
    of reChar:
      c: char
    of reCClass:
      cc: set[char]
    of reStar, rePlus, reOpt:
      child0: RegExpr
    of reCat, reAlt:
      child1, child2: RegExpr

Equality

1
2
3
4
5
6
7
8
9
proc `==`(a, b: RegExpr): bool =
  if a.kind == b.kind:
    case a.kind
    of reWordBoundary: result = true
    of reChar: result = a.c == b.c
    of reCClass: result = a.cc == b.cc
    of reStar, rePlus, reOpt: result = `==`(a.child0, b.child0)
    of reCat, reAlt: result = `==`(a.child1, b.child1) and
                              `==`(a.child2, b.child2)

Accessors

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
type
  HashTable[K, V] = object
    data: seq[(K, V)]

proc hash[K](k: K): int = 0

proc `[]`*[K, V](x: HashTable[K, V]; k: K): V =
  result = x.data[hash(k)][1]

proc `[]=`*[K, V](x: var HashTable[K, V]; k: K, v: V) =
  x.data[hash(k)][1] = v


proc initHashTable[K, V](): HashTable[K, V] =
  result.data = @[]

var tab = initHashTable[string, string]()
tab["key"] = "abc"  # calls '[]=' accessor

echo tab["key"]     # calls '[]' accessor

Accessors

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
type
  HashTable[K, V] = object
    data: seq[(K, V)]

proc hash[K](k: K): int = 0

proc `[]`*[K, V](x: HashTable[K, V]; k: K): V =
  result = x.data[hash(k)][1]

proc `[]=`*[K, V](x: var HashTable[K, V]; k: K, v: V) =
  x.data[hash(k)][1] = v


proc initHashTable[K, V](): HashTable[K, V] =
  result.data = @[]

var tab = initHashTable[string, string]()
tab["key"] = "abc"  # calls '[]=' accessor

echo tab["key"]     # calls '[]' accessor

# ouch:
tab["key"].add "xyz"

Accessors

1
2
3
4
5
6
7
8
9
proc `[]`*[Key, Value](x: var HashTable[Key, Value]; k: Key): var Value =
  result = x.data[hash(key)]


var
  tab = initHashTable[string, string]()

# compiles :-)
tab["key"].add "xyz"

Distinct

1
2
3
4
5
6
7
8
9
10
# Taken from system.nim
const taintMode = compileOption("taintmode")

when taintMode:
  type TaintedString* = distinct string
  proc len*(s: TaintedString): int {.borrow.}
else:
  type TaintedString* = string

proc readLine*(f: File): TaintedString {.tags: [ReadIOEffect], benign.}

Distinct

1
2
3
# taintmode_ex

echo readLine(stdin)
nim c -r --taintMode:on taintmode_ex

Distinct

1
2
3
# taintmode_ex

echo readLine(stdin).string
nim c -r --taintMode:on taintmode_ex

Distinct

1
2
3
4
5
# taintmode_ex

proc `$`(x: TaintedString): string {.borrow.} # but: defeats the purpose

echo readLine(stdin)
nim c -r --taintMode:on taintmode_ex

Module system

1
2
3
4
5
# Module A
var
  global*: string = "A.global"

proc p*(x: string) = echo "exported ", x
1
2
3
4
# Module B
import A

echo p(global)

Module system

1
2
3
4
5
# Module A
var
  global*: string = "A.global"

proc p*(x: string) = echo "exported ", x
1
2
3
4
# Module B
from A import p

echo p(A.global)

Module system

1
2
3
4
5
# Module A
var
  global*: string = "A.global"

proc p*(x: string) = echo "exported ", x
1
2
3
4
# Module B
import A except global

echo p(A.global)

Routines

Iterators

1
2
3
4
5
6
7
8
iterator `..<`(a, b: int): int =
  var i = a
  while i < b:
    yield i
    i += 1

for i in 0..<10:
  echo i+1, "-th iteration"

Iterators

1
2
for x in [1, 2, 3]:
  echo x

Iterators

1
2
for x in [1, 2, 3]:
  echo x

Rewritten to:

1
2
for x in items([1, 2, 3]):
  echo x

Iterators

1
2
3
4
5
iterator items*[IX, T](a: array[IX, T]): T {.inline.} =
  var i = low(IX)
  while i <= high(IX):
    yield a[i]
    i += 1

Iterators

1
2
for x in [1, 2, 3]:
  x = 0      # doesn't compile

Iterators

1
2
3
var a = [1, 2, 3]
for x in a:
  x = 0     # doesn't compile

Iterators

1
2
3
4
5
6
7
8
9
10
11
iterator mitems*[IX, T](a: var array[IX, T]): var T {.inline.} =
  var i = low(IX)
  if i <= high(IX):
    while true:
      yield a[i]
      if i >= high(IX): break
      i += 1

var a = [1, 2, 3]
for x in mitems(a):
  x = 0     # compiles

Parallelism

1
2
3
4
5
6
7
import tables, strutils

proc countWords(filename: string): CountTableRef[string] =
  ## Counts all the words in the file.
  result = newCountTable[string]()
  for word in readFile(filename).split:
    result.inc word

Parallelism

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#
#
const
  files = ["data1.txt", "data2.txt", "data3.txt", "data4.txt"]

proc main() =
  var tab = newCountTable[string]()
  for f in files:
    let tab2 = countWords(f)
    tab.merge(tab2)
  tab.sort()
  echo tab.largest

main()

Parallelism

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import threadpool

const
  files = ["data1.txt", "data2.txt", "data3.txt", "data4.txt"]

proc main() =
  var tab = newCountTable[string]()
  var results: array[files.len, FlowVar[CountTableRef[string]]]
  for i, f in files:
    results[i] = spawn countWords(f)
  for i in 0..high(results):
    tab.merge( ^results[i] )
  tab.sort()
  echo tab.largest

main()