Viktor Kirilov
C++ dev
by Viktor Kirilov
echo "Hello World"type
  # or use {.borrow.} here to inherit 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
type
  CustomRange = object
    low: int
    high: int
iterator items(range: CustomRange): int =
  var i = range.low
  while i <= range.high:
    yield i
    inc i
iterator pairs(range: CustomRange): tuple[a: int, b: char] =
  for i in range:  # uses CustomRange.items
    yield (i, char(i + ord('a')))
for i, c in CustomRange(low: 1, high: 3):
  echo c
# prints: b, c, d# This is an example how an abstract syntax tree could be modelled in Nim
type
  NodeKind = enum  # the different node types
    nkInt,          # a leaf with an integer value
    nkFloat,        # a leaf with a float value
    nkString,       # a leaf with a string value
    nkAdd,          # an addition
    nkSub,          # a subtraction
    nkIf            # an if statement
  Node = ref object
    case kind: NodeKind  # the ``kind`` field is the discriminator
    of nkInt: intVal: int
    of nkFloat: floatVal: float
    of nkString: strVal: string
    of nkAdd, nkSub:
      leftOp, rightOp: Node
    of nkIf:
      condition, thenPart, elsePart: Node
var n = Node(kind: nkFloat, floatVal: 1.0)
# the following statement raises an `FieldError` exception, because
# n.kind's value does not fit:
n.strVal = ""type
  Thing = ref object of RootObj
  Unit = ref object of Thing
    x: int
method collide(a, b: Thing) {.inline.} =
  quit "to override!"
method collide(a: Thing, b: Unit) {.inline.} =
  echo "1"
method collide(a: Unit, b: Thing) {.inline.} =
  echo "2"
var a, b: Unit
new a
new b
collide(a, b) # output: 2template withFile(f: untyped, filename: string,
                  mode: FileMode,
                  body: untyped): typed =
  let fn = filename
  var f: File
  if open(f, fn, mode):
    try:
      body
    finally:
      close(f)
  else:
    quit("cannot open: " & fn)
withFile(txt, "ttempl3.txt", fmWrite):
  txt.writeLine("line 1")
  txt.writeLine("line 2")dumpTree:
  var mt: MyType = MyType(a:123.456, b:"abcdef")
# output:
#   StmtList
#     VarSection
#       IdentDefs
#         Ident "mt"
#         Ident "MyType"
#         ObjConstr
#           Ident "MyType"
#           ExprColonExpr
#             Ident "a"
#             FloatLit 123.456
#           ExprColonExpr
#             Ident "b"
#             StrLit "abcdef"import macros
type
  MyType = object
    a: float
    b: string
macro myMacro(arg: untyped): untyped =
  var mt: MyType = MyType(a:123.456, b:"abcdef")
  let mtLit = newLit(mt)
  
  result = quote do:
    echo `arg`
    echo `mtLit`
myMacro("Hallo")
# The call to myMacro will generate the following code:
echo "Hallo"
echo MyType(a: 123.456'f64, b: "abcdef")import macros
dumpAstGen:
  proc hello() =
    echo "hi"nnkStmtList.newTree(
  nnkProcDef.newTree(
    newIdentNode(!"hello"),
    newEmptyNode(),
    newEmptyNode(),
    nnkFormalParams.newTree(
      newEmptyNode()
    ),
    newEmptyNode(),
    newEmptyNode(),
    nnkStmtList.newTree(
      nnkCommand.newTree(
        newIdentNode(!"echo"),
        newLit("hi")
      )
    )
  )
)import macros
macro gen_hello(): typed =
  result = nnkStmtList.newTree(
    nnkProcDef.newTree(
      newIdentNode(!"hello"),
      newEmptyNode(),
      newEmptyNode(),
      nnkFormalParams.newTree(
        newEmptyNode()
      ),
      newEmptyNode(),
      newEmptyNode(),
      nnkStmtList.newTree(
        nnkCommand.newTree(
          newIdentNode(!"echo"),
          newLit("hi")
        )
      )
    )
  )
gen_hello()
hello() # << same as from last slide!import html_dsl
html page:
  head:
    title("Title")
  body:
    p("Hello")
    p("World")
    dv:
      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, initial-scale=1">
    <title>Title</title>
  </head>
  <body class='has-navbar-fixed-top' >
    <p >Hello</p>
    <p >World</p>
    <div>
      <p>Example</p>
    </div>
  </body>
</html># main.nim
import a
echo a()# a.nim
import b
proc a*(): string =
  return from_b# b.nim
let local = "B!"
let from_b* = 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
proc foo() =
  echo "hello"
foo()#include <nimbase.h>
// forward declarations / type definitions / constants section
struct TGenericSeq { int len; int reserved; };
struct NimStringDesc : public TGenericSeq { ... };
typedef NimStringDesc* tyArray_nHXaesL0DJZHyVS07ARPRA[1];
STRING_LITERAL(TM_r9bkcJ6PRJ5n7ORNxxJ5ryg_3, "hello", 5); // << string literal
NIM_CONST tyArray_nHXaesL0DJZHyVS07ARPRA TM_r9bkcJ6PRJ5n7ORNxxJ5ryg_2 =
    {((NimStringDesc*) &TM_r9bkcJ6PRJ5n7ORNxxJ5ryg_3)};
N_LIB_PRIVATE N_NIMCALL(void, foo_iineYNh8S9cE6Ry7dr2Tz2A)(void); // << fwd decl
// definition section
N_LIB_PRIVATE N_NIMCALL(void, foo_iineYNh8S9cE6Ry7dr2Tz2A)(void) { // << def
    echoBinSafe(TM_r9bkcJ6PRJ5n7ORNxxJ5ryg_2, 1); // the echo call
}
// code execution section
foo_iineYNh8S9cE6Ry7dr2Tz2A(); // << calltype
  MyData = object
    answer: int
    ready: bool
proc newData(): MyData = return MyData(answer: 42, ready: true)
echo newData().answer// forward declarations / type definitions / constants section
struct tyObject_MyData {
    int answer;
    bool ready;
};
// definition section
N_LIB_PRIVATE N_NIMCALL(tyObject_MyData, newData)(void) {
    tyObject_MyData result; // always an implicit "result"
    nimZeroMem((void*)(&result), sizeof(tyObject_MyData));
    result.answer = ((int) 42);
    result.ready = true;
    return result;
}
// code execution section
tyObject_MyData T2_;
T2_ = newData(); // << call
//...iterator closure_iter*(): int {.closure.} = # a resumable function
  var x = 1
  while x < 10:
    yield x
    inc x
for i in closure_iter(): echo istruct state_type : public RootObj {
    int colonstate_; // state progress - there are some GOTOs using this
    int x1; // the state
};
struct closure_type {
    N_NIMCALL_PTR(int, c_ptr) (void* e_ptr); // function ptr
    void* e_ptr; // environment ptr
};
N_LIB_PRIVATE N_CLOSURE(int, func)(void* e_ptr) { // def omitted for simplicity
state_type st; // the state
closure_type local; // the closure
local.c_ptr = func; // assign the func
local.e_ptr = &st; // assign environment
//...
i = local.c_ptr(local.e_ptr); // the call in the loopproc printf(formatstr: cstring)
    {.header: "<stdio.h>", importc: "printf", varargs.}{.emit: """
using namespace core;
""".}
{.compile: "logic.c".}other 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>c2nim tool - generate C/C++ bindings for Nim
type
  StdMap {.importcpp: "std::map", header: "<map>".} [K, V] = object
proc `[]=`[K, V](this: var StdMap[K, V]; key: K; val: V) {.
  importcpp: "#[#] = #", header: "<map>".}
var x: StdMap[cint, cdouble]
x[6] = 91.4std::map<int, double> x;
x[6] = 91.4;C++ template constructs
Generated C++
# main.nim
import hotcodereloading # for reload
import other
while true:
  echo readLine(stdin) # pause
  performCodeReload()  # reload
  echo getInt()        # call# other.nim
import hotcodereloading # for after handler
var glob = 42
proc getInt*(): int = return glob + 1 # exported
afterCodeReload:
  glob = 666built as an .exe/.dll depending on the project type
built as a reloadable .dll
ends up in the "nimcache"
// fwd decl/globals section
static N_NIMCALL_PTR(int, getInt_omy6T2FkprLEReOy2ITmIQ)(void);
static int* glob_v1zK9aUOu9aNNcsxruuK8NdA;
// definitions
N_LIB_PRIVATE N_NIMCALL(int, getInt_omy6T2FkprLEReOy2ITmIQ_actual)(void) {
	int result;                                       // ^^ the suffix
	result = (*glob_v1zK9aUOu9aNNcsxruuK8NdA);
	return result;
}
// usage
(*glob_v1zK9aUOu9aNNcsxruuK8NdA) = getInt_omy6T2FkprLEReOy2ITmIQ();
// init on startup (naive)
glob_v1zK9aUOu9aNNcsxruuK8NdA = new int(42);
getInt_omy6T2FkprLEReOy2ITmIQ = getInt_omy6T2FkprLEReOy2ITmIQ_actual// naive
glob_v1zK9aUOu9aNNcsxruuK8NdA = new int(42);
getInt_omy6T2FkprLEReOy2ITmIQ = getInt_omy6T2FkprLEReOy2ITmIQ_actual// reality
getInt_omy6T2FkprLEReOy2ITmIQ = (tyProc_vVu2P82aVLv9c8X0xbI1NJw) hcrRegisterProc(
    "D:\\play\\nimcache/play.cpp.dll",            // "domain" (AKA module)
    "getInt_omy6T2FkprLEReOy2ITmIQ",              // "key"
    (void*)getInt_omy6T2FkprLEReOy2ITmIQ_actual); // the real function
if(hcrRegisterGlobal("D:\\play\\nimcache/play.cpp.dll",        // "domain" (AKA module)
                     "glob_v1zK9aUOu9aNNcsxruuK8NdA",          // "key"
                     sizeof((*glob_v1zK9aUOu9aNNcsxruuK8NdA)), // size for allocation
                     NULL, // for the GC - simple integer is simple, so NULL
                     (void**)&glob_v1zK9aUOu9aNNcsxruuK8NdA))  // address to pointer
{
    // hcrRegisterGlobal returns "true" only if not already inited
    (*glob_v1zK9aUOu9aNNcsxruuK8NdA) = ((int) 42); // init with value (or side effects)
}# main.nim
import a, b
echo from_a()
echo from_b()# a.nim
import b
proc from_a*(): string =
  result = "A!"
  result.add from_b()# b.nim
proc from_b*(): string =
  return "B!"when we call performCodeReload():
# main.nim
import a, b, hotcodereloading
beforeCodeReload:
  echo "before main"
afterCodeReload:
  echo "after main"# a.nim
import b, hotcodereloading
beforeCodeReload:
  echo "before a"
afterCodeReload:
  echo "after a"# b.nim
import hotcodereloading
beforeCodeReload:
  echo "before b"
afterCodeReload:
  echo "after b"only A changes => all handlers are executed on reload:
before b
before a
before main
after b
after a
after main# main.nim
import hotcodereloading # for reload
import other
while true:
  echo readLine(stdin) # pause
  performCodeReload() # reload
  echo getInt() # call# other.nim
import hotcodereloading # for after handler
var glob = 42
proc getInt*(): int = return glob # exported
afterCodeReload:
  glob = 666Makes more sense now, doesn't it?
.dll/.exe have hardcoded paths to the .pdb (copying the .dll doesn't matter)
the VS Debugger keeps the .pdb files locked for .dlls even after unloaded
someone managed to close the file handles to no longer needed .pdb files (.dll has been unloaded) to the external VS debugger process (live++)
embed the debug info in the actual binaries just like on unix
/Z7 embeds it in .obj files but not for the final .dll/.exe when linking them
solutions:
for reference: zlib (c code) to javascript (asm.js) ==> x2 slow down
calls within a translation unit are direct (the "_actual" version gets called)
calls between modules => indirection: pointer to function
+ additional jump from trampoline to actual function
not going through function pointers
by default there are no jumps in the function preamble (padding)
possible optimizations:
Nim is the language I have always thought was a brilliant idea that I never get to use. It's a shame.
Nim is to C/C++ as CoffeeScript is to JavaScript. A highly extensible template language atop a portable language with libraries for practically everything.
So why haven't I hopped on the bandwagon? Outside of C++, C, and Fortran - the only way I have ever learned a new language is through using a REPL. How much of Python's and MATLAB's (and maybe even Julia's) success is due to having a brilliant REPL?
I am not complaining, and I do not have any free time to fix it. But man... if Nim just had a killer REPL that allowed me to slowly learn the language properly while not being blocked from my daily work... it would be just killer!
cjhanks on Apr 18, 2017
2 files:
Talk abstract was a lie! didn't get to implementing it in time...
should be well below half a second
you submit this:
import tables
var a = {1: "one", 2: "two"}.toTable
echo aand it gets translated to this:
import hotcodereloading # for the before/after handlers
import tables
var a = {1: "one", 2: "two"}.toTable
afterCodeReload:
  echo alater you append:
let b = a
echo band it gets translated to this:
import hotcodereloading # for the before/after handlers
import tables
var a = {1: "one", 2: "two"}.toTable
let b = a # the new code
# only the new side effects are still present
afterCodeReload:
  echo b
By Viktor Kirilov
About the Nim programming language and the implementation of this feature: https://github.com/nim-lang/Nim/issues/8927