by Viktor Kirilov
subtitle: every talk I make is a lightning talk
and this one is going to be intense - sorry :|
echo "Hello World" # codenim c hello.nim # compile./hello         # runHello World     # stdoutimport strformat # can also import specific parts and give aliases
type
  Person = object
    name: string
    age: Natural # Ensures the age is positive
# Types in Nim: arrays, sequences, tuples, sets & objects
let people = [
  Person(name: "John", age: 45),
  Person(name: "Kate", age: 30)
]
# Proc means function
proc doWork() =
  # Indentation based - like Python
  for person in people:
    # Type-safe string interpolation, evaluated at compile time.
    echo(fmt"{person.name} is {person.age} years old")
doWork() # and we get some output# This is an example how an abstract syntax tree could be modelled in Nim
type
  NodeKind = enum # the different node types
    Int,    # a leaf with an integer value
    String, # a leaf with a string value
    Add,    # an addition
    Sub,    # a subtraction
    If      # an if statement
  Node = ref object
    case kind: NodeKind # the ``kind`` field is the discriminator
    of Int: int_val: int
    of String: str_val: string
    of Add, Sub: # '+' or '-'
      left, right: Node
    of If:
      condition, then_part, else_part: Node
var n = Node(kind: Int, int_val: 42)
if n.kind notin {Add, Sub, If}: # easy work with sets/flags/enumerations
  echo "it's a value!"
# raises a `FieldError` exception, because n.kind is Int and not String
n.str_val = ""
type
  Note* = ref object of RootObj
    text: string
  TaskNote* = ref object of Note
    completed: bool
method render*(note: Note): string = # the object is the first argument
  return note.text
method render*(note: TaskNote): string =
  case note.completed
  of true: return "☑ " & note.text
  of false: return "☐ " & note.text
var baseType: Note = TaskNote(text: "Do me soon!", completed: false)
echo baseType.render() # polymorphic call - outputs "☐ Do me soon!"proc writeToConsole() =
  echo "is IO a side effect?"
var glob = 5
proc touchGlobal() =
  glob = 6
proc impossible() {.noSideEffect.} = # will be checked within the whole program
  writeToConsole() # this won't compile because of the "echo"
  touchGlobal() # this won't compile either - because of the global var access
proc complex() {.raises: [IOError, ArithmeticError].} = # only these can be thrown
  #...
proc simple() {.raises: [].} = # no exceptions can pass through
  #...# example defining a currency
type
  # or use {.borrow.} here to reuse everything
  Dollars = distinct float
proc `+`(a, b: Dollars): Dollars {.borrow.}
var a = 20.Dollars
a = 25  # Doesn't compile
a = 25.Dollars  # Works fine
a = 20.Dollars * 20.Dollars # Doesn't compile
a = 20.Dollars + 20.Dollars # Works fine
while b ≠ 0
  if a > b
    a := a − b
  else
    b := b − a
return atemplate withLock(lock: Lock, body: untyped) =
  acquire lock
  try:
    body # <<< this is where the 'block' of code will go
  finally:
    release lockvar ourLock: Lock # create a lock
initLock ourLock  # init the lock
withLock ourLock: # here we use the template and pass a code block
  echo "Do something which requires locking"
  echo "This might throw an exception"var ourLock: Lock # this code was written out of the template
initLock ourLock  # this code was written out of the template
acquire ourLock # the result after the substitution - no "template call"
try:
  echo "Do something that requires locking"
  echo "This might throw an exception"
finally:
  release ourLocktemplate withFile(f_var: untyped,   # name of the file variable
                  filename: string, # file to open
                  mode: FileMode,   # file open mode
                  body: untyped) =  # the block of code
  let fn = filename # to prevent double evaluation of 'filename'
  var f_var: File
  if open(f_var, fn, mode): # first use of 'fn'
    try:
      body # <<< this is where the 'block' of code will go
    finally:
      close(f_var)
  else:
    quit("cannot open: " & fn) # second use of 'fn'
withFile(txt, "ttempl3.txt", fmWrite): # f_var will be 'txt'
  txt.writeLine("line 1")
  txt.writeLine("line 2")# match multiplication of integers and the literal '2'
template optimMultiply{`*`(a, 2)}(a: int): int = a + a
let x = 3
# will use addition instead of multiplication - not
# really useful since the C/C++ optimizer would handle
# this specific case, but this showcases the power of Nim
echo x * 2
# not going to match this because it's '3' and not '2'
echo x * 3# this definition exists in the System module
template `!=` (a, b: untyped): untyped =
  not (a == b)
assert(5 != 6) # the compiler rewrites that to: assert(not (5 == 6))import macros # 'macros' module for working with the AST
# inspect the AST hierarchy of any block of code
dumpTree:
  var mt: MyType = MyType(a:123.456, b:"abcdef")# the output from dumpTree
StmtList               # list ot statements
  VarSection           # var statement
    IdentDefs          # identifier definition
      Ident "mt"       # name of var - an identifier
      Ident "MyType"   # type of var - an identifier
      ObjConstr        # constructor
        Ident "MyType" # identifier to call for construction
        ExprColonExpr  # ... you get the picture
          Ident "a"
          FloatLit 123.456
        ExprColonExpr
          Ident "b"
          StrLit "abcdef"import macros, strutils # 'macros' module for manipulating the AST
macro toEnum(words: static[string]): untyped =
  # in Nim procs and macros have an implicit "result" variable
  # here we create a new tree node of type 'Enumeration'
  result = newTree(nnkEnumTy, newEmptyNode())
  # we split the words string on whitespace and iterate over them
  for w in splitWhitespace(words):
    # and we add identifier nodes as children to the enumeration
    result.add ident(w)
type
  Color = toEnum"Red Green Blue Indigo"
# Indigo is a valid identifier after the NimVM
# has gone through the call to "toEnum"
var color = Indigoimport macros # 'macros' module for working with the AST
dumpAstGen: # this will dump Nim code to create the code we have passed it
  proc hello() =
    echo "hi"nnkStmtList.newTree( # the procedure definition statement
  nnkProcDef.newTree( # the procedure definition
    newIdentNode(!"hello"), # identifier - the name
    newEmptyNode(),
    newEmptyNode(),
    nnkFormalParams.newTree( # no parameters
      newEmptyNode()
    ),
    newEmptyNode(), # empty (for pragmas, etc.) - our 'hello' proc is too simple
    newEmptyNode(),
    nnkStmtList.newTree( # the list of statements in the proc
      nnkCommand.newTree( # a function call
        newIdentNode(!"echo"), # we call 'echo'
        newLit("hi") # and we pass it 'hi'
      )
    )
  )
)import macros # 'macros' module for working with the AST
macro generate_hello(): typed =
  result = nnkStmtList.newTree( # the output from dumpAstGen (from last slide)
    nnkProcDef.newTree(
      newIdentNode(!"hello"),
      newEmptyNode(),
      newEmptyNode(),
      nnkFormalParams.newTree(
        newEmptyNode()
      ),
      newEmptyNode(),
      newEmptyNode(),
      nnkStmtList.newTree(
        nnkCommand.newTree(
          newIdentNode(!"echo"),
          newLit("hi")
        )
      )
    )
  )
generate_hello() # create the hello() proc from the last slide
hello() # hello() now exists!!! and when called will print "hi"import macros # 'macros' module for manipulating the AST
type
  MyType = object # some random type with 2 fields
    a: float
    b: string
macro myMacro(arg: untyped): untyped =
  var mt: MyType = MyType(a:123.456, b:"abcdef") # an arbitrary value
  
  let mtLit = newLit(mt) # convert the value into a NimNode tree
  
  # here we put literally the code we want
  # we inject NimNode symbols with backticks
  result = quote do:
    echo `arg`
    echo `mtLit`
myMacro("Hallo") # call the bad boy
# The call to myMacro will generate the following code:
echo "Hallo"
echo MyType(a: 123.456'f64, b: "abcdef")import html_dsl
# no need for third-party
# templating engines
html page:
  head:
    title("Title")
  body:
    p("Hello")
    p("World")
    dv:
      # we can mix code with
      # the HTML view
      for i in 0..3:
        p "Example"
echo render(page())<!DOCTYPE html>
  <html class='has-navbar-fixed-top'>
  <head>
    <meta charset="utf-8">
    <meta name="viewport"
          content="width=device-width">
    <title>Title</title>
  </head>
  <body class='has-navbar-fixed-top'>
    <p >Hello</p>
    <p >World</p>
    <div>
      <p>Example</p>
      <p>Example</p>
      <p>Example</p>
    </div>
  </body>
</html>import protobuf # module can be fetched from the package manager
# Define our Protobuf specification
const protoSpec = """syntax = "proto3";
message ExampleMessage {
  int32 number = 1;
  string text = 2;
  SubMessage nested = 3;
  message SubMessage {
    int32 a_field = 1;
  }
}
"""
# Generate Nim code to use it - invoking a full Protobuf parser
parseProto(protoSpec)
# Create messages using the already constructed Nim types
var msg = new ExampleMessage
msg.number = 10
msg.text = "Hello world"
# We even have helper functions for working with sub-messages
msg.nested = initExampleMessage_SubMessage(aField = 100)# main.nim
import foo
# there is no main() function
echo from_foo()
echo "main" # global scope code# foo.nim
import bar
proc from_foo*(): string =
  return from_bar
echo "foo" # global scope code# bar.nim
let local = "BAR!"
let from_bar* = local
            ^
# '*' means 'exported'// nimbase.h
#define N_NIMCALL(rettype, name) rettype __fastcall name
#define N_CDECL(rettype, name) rettype __cdecl name
//...
#define N_NIMCALL_PTR(rettype, name) rettype (__fastcall *name)
//...
#define N_LIB_PRIVATE __attribute__((visibility("hidden")))
//...
#define N_LIB_EXPORT  extern __declspec(dllexport)
//...
#define STRING_LITERAL(name, str, length) \
   static const struct {                  \
     TGenericSeq Sup;                     \
     char data[(length) + 1];             \
  } name = {{length, (int) ((unsigned)length | NIM_STRLIT_FLAG)}, str}included by all .c/.cpp files
handles different platforms - convenience macros
# foo.nim
proc foo() =
  echo "hello"
foo()// == includes section
#include <nimbase.h> // nimbase is always present
// == type definitions section
struct TGenericSeq { int len; int reserved; };
struct NimStringDesc : public TGenericSeq { ... };
// == constants & globals section
STRING_LITERAL(the_string_literal, "hello", 5);
NimStringDesc the_string_constant(the_string_literal);
// == forward declarations section
void foo_iineYNh8S9cE6Ry7dr2Tz2A(); // mangled name
// == definitions section
void foo_iineYNh8S9cE6Ry7dr2Tz2A() {
    echoBinSafe(the_string_constant, 1); // the echo call
}
// == init section
void init_module_foo() {
    foo_iineYNh8S9cE6Ry7dr2Tz2A(); // << call
}
// == other sections - omitted for simplicity
// ...type
  MyData = object
    answer: int
    ready: bool
proc newData(): MyData = return MyData(answer: 42, ready: true)
echo newData().answer// == type definitions section
struct tyObject_MyData {
    int answer;
    bool ready;
};
// == definitions section
tyObject_MyData newData() {
    tyObject_MyData result; // always an implicit "result"
    nimZeroMem((void*)(&result), sizeof(tyObject_MyData));
    result.answer = ((int) 42);
    result.ready = true;
    return result;
}
// == init section
void init_module_foo() {
    tyObject_MyData T2_;
    T2_ = newData(); // << call the construction
    echoBinSafe(T2_.answer, 1); // the echo call
}iterator closure_iter*(): int {.closure.} = # a resumable function with state
  var x = 1
  while x < 10:
    yield x
    inc x
for i in closure_iter(): echo i// == type definitions section
struct state_type : public RootObj {
    int colonstate_; // state progress - there are some GOTOs using this
    int x1; // the local variable state
};
struct closure_type {
    int (*c_ptr)(state_type* state); // function ptr
    void* state; // state ptr
};
int closure_iter(state_type* state) {
    switch (state->colonstate_) // switch/GOTO - doesn't matter
    // ... a state machine - definition omitted for simplicity
}
// == init section
state_type st; // the state - should outlive the closure (pair of pointers)
closure_type cl; // the closure - can be passed around
cl.c_ptr = closure_iter; // assign the function pointer
cl.state = &st; // assign pointer to state
// ...
int i = cl.c_ptr(cl.state); // the call in the loopproc printf(formatstr: cstring)
    {.header: "<stdio.h>", importc: "printf", varargs.}{.emit: """
using namespace core;
""".} # emits C++ code! can also do inline assembly
{.compile: "logic.c".} # compile & link this .c fileother pragmas - for use in Nim:
We can also call Nim code from C/C++:
# fib.nim
proc fib(a: cint): cint {.exportc.} # do not manglenim c --noMain --noLinking --header:fib.h fib.nim// user.c
#include <fib.h>type # here we define a generic type which maps directly to std::map
  StdMap {.importcpp: "std::map", header: "<map>".} [K, V] = object
proc `[]=`[K, V](this: var StdMap[K, V]; key: K; val: V) {.
  importcpp: "#[#] = #", header: "<map>".} # we import the [] operator
var x: StdMap[cint, cdouble] # and we use it directly...
x[6] = 91.4std::map<int, double> x;
x[6] = 91.4; // no binding layer - C++ types/functions are called directlyC++ template constructs
Generated C++
import threadpool # high-level threading API
# create and open the string channel for message passing
var chan: Channel[string]
open(chan)
# a procedure which communicates through the channel
proc sayHello() =
  chan.send("Hello")
# spawn a thread executing the sayHello procedure
spawn sayHello()
# we can receive whatever the spawned thread has sent to us
echo chan.recv()import dom # only for the javascript backend
proc onLoad(event: Event) =
  let p = document.createElement("p")
  p.innerHTML = "Click me!"
  p.style.fontFamily = "Helvetica"
  p.style.color = "red"
  p.addEventListener("click",
    proc (event: Event) =
      window.alert("Hello World!")
  )
  document.body.appendChild(p)
window.onload = onLoadimport htmlgen, jester, re # html, server & regular expressions
routes:
  get "/hello/@name?":
    # This matches "/hello/fred" and "/hello/bob" => ``@"name"`` will
    # be either "fred" or "bob". It will also match "/hello/".
    if @"name" == "":
      resp "No name received :("
    else:
      resp "Hello " & @"name"
  
  # Matches URLs of the form /15.html => request.matches[0] will be 15.
  get re"^\/([0-9]{2})\.html$":
    resp request.matches[0]
  
  # A greeting for the root
  get "/":
    resp h1("Hello world")% of memory safety vs. non-memory safety CVEs by patch each year
Memory-safe languages suffer too - node.js modules are written in C
Linear growth - slow
Rust is just being Rust :|
A bit more curved than Rust - same 9 year span - more momentum!
Can you understand my bias now? :)
guilty of not talking about functional languages :|