ted.neward@newardassociates.com | Blog: http://blogs.newardassociates.com | Github: tedneward | LinkedIn: tedneward
Code: https://github.com/tedneward/Demo-Nim
Slides: http://www.newardassociates.com/presentations/BusyDevsGuide/Nim.html
Learn how to get started with Nim
Find out why Nim is interesting
See Nim syntax and semantics
Nim is a statically typed compiled systems programming language. It combines successful concepts from mature languages like Python, Ada and Modula.
https://nim-lang.org/
statically-typed
compiled to native ("native dependency-free") or JavaScript
memory management is deterministic and customizable
compile-time evaluation of some constructs
support for different backends (C, C++, JavaScript)
macro system offering full AST manipulation (at compile-time)
Download: https://nim-lang.org/install.html
Homebrew/Linuxbrew: brew install nim
https://play.nim-lang.org/
Hello world
# This is a comment echo "What's your name? " var name: string = readLine(stdin) echo "Hi, ", name, "!"
Compiling, then executing
$ nim c hello.nim $ ./hello What's your name? Ted Hi, Ted!
Compiling and executing in one run
$ nim compile -r hello.nim
nim c is transforming .nim file into .c file and then compiling with C compiler
nim cpp hello.nim uses C++ compiler
nim objc hello.nim uses ObjC compiler
nim js generates JavaScript (1.5) code (one long .js file)
there are some restrictions to standard library use
temporary/generated files find a home in ~/.cache/nim
sorted by nim filename + _d (debug) or _r (release)
basics.nim -> ~/.cache/nim/basics_d/@mbasics.nim.c
... plus whatever other supporting Nim files were used
these files are NOT pretty
Single-line comments start with #
Documentation comments start with ##
Multi-line comments bracketed with #[ and ]#
and can be nested
Keywords can be used as identifiers if backtick-enclosed
Pragmas are {. ... .}-bracketed
Syntax
# This is a comment
#[ This is
a multiline
comment ]#
# var and let are both keywords but here we use them as identifiers
var `var` = 42
let `let` = 8
assert `var` + `let` == 50
declaration name : type = value
Declarations
var: mutable/variable
const: compile-time-known immutable binding
let: non-compile-known immutable binding
names are UTF-8 strings
case- and underscore-insensitive, except for first character
type is optional if value is provided (type-inference)
Multiple declaration syntax is supported
Declarations in Nim
var a: int # mutable integer, no value explicitly set (0)
var b = 7 # mutable inferred integer set to 7
var
c = -11 # mutable inferred integer set to -11
d = "Hello" # mutable inferred string set to "Hello"
e = '/' # mutable inferred character set to '/'
var f1, f2 = 37 # mutable inferred integer set to variables
const g = 35 # immutable integer set to compile-time value
var k = 27 # mutable inferred integer set to 27 (runtime value)
let j = 2 * k # immutable integer set to runtime value
let l = 128'i16 # mutable 16-bit integer
true, false literals
Relational operators:
==: equality
!=: inequality
<, <=, >, >=, per usual
Logical operators:
and: both operands must be true
or: either operand can be true
xor: true if only one operand is true
not: negates the truth value of the operand
Booleans in Nim
let
rg = 31
rh = 99
echo "rg is greater than rh: ", rg > rh
echo "rg is smaller than rh: ", rg < rh
echo "rg is equal to rh: ", rg == rh
echo "rg is not equal to rh: ", rg != rh
echo "T and T: ", true and true
echo "T or F: ", true or false
echo "F xor F: ", false xor false
echo "not F: ", not false
int, intN where N is 8, 16, 32, 64
uint, uintN where N is 8, 16, 32, 64
literals use _ as thousands separator
usual mathematical operators: +, -, *, /
+, -, * produce integer result
/ produces floating-point result
div produces integer (no-remainder) result
mod produces modulo (remainer-only) result
float, f, f32, d, f64
follows floating-point standard
literals support scientific (exponentiation) notation: 4e7
usual mathematical operators: +, -, *, /
no div or mod
like a procedure call, type name replacing the procedure name
always safe: failure to convert results in an exception
int() converts parameter to int type
float() convers parameter to float type
idiomatically Nim prefers ordinary proc calls over type conversions
Nim numerics
let na = 11 var nb1 = (na + 5 - (3 * 2)) / 3 var nb2 = (na + 5 - (3 * 2)) div 3 var nb3 = (na + 5 - (3 * 2)) mod 3 var nc = int(5.5) var nd = float(5) #var ne = nc + nd # Error: type mismatch
want an unsigned 4-bit integer? maybe called u4?
create a callable identifier (proc, template, macro)
name it 'u4 (the leading ' is required)
take a string, return the new literal value
Nim custom numerics
import std/strutils
type u4 = distinct uint8 # a 4-bit unsigned integer aka "nibble"
proc `'u4`(n: string): u4 =
# The leading ' is required.
result = (parseInt(n) and 0x0F).u4
var x = 5'u4
single ASCII character
multiple-character literals produce error
literals are offset by ticks (')
special characters:
\n: newline character
\t: tab character
\: backslash character
series of characters
literals are enclosed in double quotes (")
convert values to strings via $
concatenation via &
special characters also supported
"raw" r-prefixed strings ignore special characters
"long strings" are """-enclosed strings
strings are objects
Nim strings and characters
var sa = "Hello"
let sb = sa & "\nworld\n"
let sr = r"Hello\tworld" # raw string: "Hello world"
sa.add(" the world")
echo "The sa string equals", sa
let
ci = 'a'
cj = 'd'
echo ci < cj
let
sm = "axyb"
sn = "axyz"
so = "ba"
sp = "ba "
echo sm < sn
echo sn < so
echo so < sp
Enums, ints, char and bool (and subranges)
ordinal types have special operations:
ord(x): returns the integer value that is used to represent x's value
inc(x): increments x by one; inc(x, n): increments x by n; n is an integer
dec(x): decrements x by one; dec(x, n): decrements x by n; n is an integer
succ(x): returns the successor of x; succ(x, n): returns the n'th successor of x
pred(x): returns the predecessor of x; pred(x, n): returns the n'th predecessor of x
all these can fail w/ RangeDefect or OverflowDefect if relevant runtime checks on
type aliases
type-safe; impossible to implicitly coerce a distinct type into its base
type name = distinct type
none of the base type's procedures follow
"borrow" pragma can automate generation of procedures
Distinct Types
type
Dollars* = distinct float
proc `*` *(a, b: Dollars): Dollars {.borrow.}
proc `+` *(a, b: Dollars): Dollars {.borrow.}
var usd = 20.Dollars
#usd = 25 # Doesn't compile
usd = 25.Dollars # Works fine
usd = 20.Dollars * 20.Dollars
echo usd.float
# BTW, reminder: Never use floating-point values for money!
Enumerations
Subrange types
Arrays and "openarrays"
Sequences
Tuples
bound set of constant values, type-checked
type name = enum \n id1, id2, id3 ...
enum identifiers may not need type-qualifier prefixes unless {.pure.} follows name
enum identifiers may be given explicit values (e.g. type Colors = enum Red = "FF0000")
Enums
type CompassDirections = enum cdNorth, cdEast, cdSouth, cdWest
type
Colors {.pure.} = enum
Red = "FF0000", Green = (1, "00FF00"), Blue = "0000FF"
OtherColors {.pure.} = enum
Red = 0xFF0000, Orange = 0xFFA500, Yellow = 0xFFFF00
Signals = enum
sigQuit = 3, sigAbort = 6, sigKill = 9
echo "Go ", cdWest, ", young man!"
# echo Red # Error: ambiguous identifier: 'Red'
echo Colors.Red # FF0000
echo OtherColors.Red # Red
echo OtherColors.Orange, " ", Orange # Orange Orange
echo int(sigQuit), " ", sigAbort, " ", float(sigKill) # 3 sigAbort 9.0
range of values from an integer or enumeration type ("base type")
type name = range[ lowValue .. highValue ]
essentially an enumeration out of a subset of the base type's range of possible values
compile-time and runtime checked
base type and subrange type are type-compatible
Natural type is range[0..high(int)] (where high is maximal integer value)
Subrange types
type Age = range[0..125]
var sra : Age
var srb = sra
var src = sra - 45 # Runtime error! (But only if checks are on)
#var srd : Age = 595 # Compile error!
echo src
type Grade = range['A'..'F']
var srg : Grade = 'F'
"homogeneous container of constant length"
size must be known at compile-time
array[length,type] = [values]
values are comma-separated list of values
length and type can be inferred from values
items, mitems returns immutable and mutable element sequence
pairs, mpairs returns element, index sequence pairs
Arrays
var
a: array[3, int] = [5, 7, 9]
b = [5, 7, 9]
# c = [] # error
d: array[7, string]
const m = 3
let n = 5
var ba: array[m, char]
"homoegeneous container of flexible length"
size doesn't have to be known at compile-time
size can change at runtime
seq[type] = @[values]
if values are present, sequence declaration can be inferred
or newSeq[type]() procedure call
Sequences
var
e1: seq[int] = @[]
f = @["abc", "def"]
e = newSeq[int]()
g = @['x', 'y']
h = @['1', '2', '3']
g.add('z') # x, y, z
h.add(g) # 1, 2, 3, x, y, z
var i = @[9, 8, 7] # 3
i.add(6) # 4
fixed-size arrays are often too inflexible
procedures should be able to deal with differing-sized arrays
openarray[type] parameter declarations allows this
openarrays support len, low, and high operations
arrays of compatible type can be passed where openarrays are expected
sequences can also be passed
both arrays and sequences use [-] indexing syntax
positive index starts 0-indexed from the left
^-prefixed index starts 1-indexed from the right
start .. end (range) is a "slice", inclusive of both ends
start ..< end is a "slice", exclusive of end
Indexing and slicing
let j = ['a', 'b', 'c', 'd', 'e']
echo j[1] # a
echo j[^1] # e
echo j[0 .. 3] # @[a, b, c, d]
echo j[0 ..< 3] # @[a, b, c]
heterogenous mutable-contents fixed-size containers
type name = tuple[ name : type , ... ]
structural (not nominative) type-checking
( values ) where values is a comma-separated list of values
( name : value ) named-field tuple
access fields by name or position
[ index ]
.name
Tuples
let t = ("Banana", 2, 'c')
echo t # (Field0: "Banana", Field1: 2, Field2: 'c')
var o = (name: "Banana", weight: 2, rating: 'c')
o[1] = 7
o.name = "Apple"
echo o # (name: "Apple", weight: 7, rating: 'c')
type Fruit = tuple[name: string, weight: int, rating: char]
var u = (name: "Kumquat", weight: 5, rating: 'f')
echo u
statements can occur in an expression context, separated by semicolons
(stmt1; stmt2; stmt3; ex) is a statement list expression
the resulting type is the type of ex
all the other statements must be of type void
parentheses appear to sometimes be optional; not mentioned in manual
Statement list expression
a = if a > 15: (echo "Big!"; 5) else: 10 echo a
if condition :
elif condition : (optional)
else:
indentation-based scope blocks
expression -- yields a result
If
var x = 10
if x < 5:
echo "x is less than 5"
elif x > 5:
echo "x is greater than 5"
else:
echo "x is equal to 5"
var y = if x > 8: 9 else: 10
case expression
of value :
else: (optional)
elif: (optional)
each of block has indentation scope
also an expression -- yields a result
Case
let i = 7
case i
of 0:
echo "i is zero"
of 1, 3, 5, 7, 9:
echo "i is odd"
of 2, 4, 6, 8:
echo "i is even"
else:
echo "i is too large"
# {{## END case-1 ##}}
# {{## BEGIN case-2 ##}}
var favoriteFood = 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"
# {{## END case-2 ##}}
echo animal, " likes ", favoriteFood
# {{## BEGIN for ##}}
for n in 5 ..< 9:
echo n
for n in countup(0, 16, 4):
echo n
for index, item in ["a","b"].pairs:
echo item, " at index ", index
# {{## END for ##}}
# {{## BEGIN while ##}}
var a = 1
while a*a < 100:
echo "a is: ", a
inc a
echo "final value of a: ", a
# {{## END while ##}}
# {{## BEGIN when ##}}
when system.hostOS == "windows":
echo "running on Windows!"
elif system.hostOS == "linux":
echo "running on Linux!"
elif system.hostOS == "macosx":
echo "running on Mac OS X!"
else:
echo "unknown operating system"
# {{## END when ##}}
# {{## BEGIN block ##}}
block myblock:
echo "entering block"
while true:
echo "looping"
break # leaves the loop, but not the block
echo "still in block"
echo "outside the block"
# {{## END block ##}}
# {{## BEGIN exception-handling ##}}
var f: File
if open(f, "quote.txt"):
try:
echo readLine(f)
except IOError:
echo "IO error!"
except CatchableError:
echo "Unknown exception!"
# reraise the unknown exception:
raise
except: # catch all other exceptions
echo "Other error!"
finally:
close(f)
# {{## END exception-handling ##}}
# {{## BEGIN exception-tracking ##}}
# {{## END exception-tracking ##}}
# {{## BEGIN statement-list ##}}
a = if a > 15: (echo "Big!"; 5) else: 10
echo a
# {{## END statement-list ##}}
Case
var favoriteFood = 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"
almost identical to if, except
each condition must be a constant/compiler-evaluatable
statements within a branch do not open new scope
compiler produces code only for the statements that belong to the first condition
comparable to #ifdef in other languages
When
when system.hostOS == "windows": echo "running on Windows!" elif system.hostOS == "linux": echo "running on Linux!" elif system.hostOS == "macosx": echo "running on Mac OS X!" else: echo "unknown operating system"
while condition :
opens new (indented) scope block
While
var a = 1
while a*a < 100:
echo "a is: ", a
inc a
echo "final value of a: ", a
for iterator in sequence
sequence must be iterable, producing an iterator
m .. n: range m to n, inclusive
m ..< n: range m to n-1
scope block (indented) executes on each iteration
iterator can be a tuple pair depending on the sequence
index, item from arrays
For
for n in 5 ..< 9:
echo n
for n in countup(0, 16, 4):
echo n
for index, item in ["a","b"].pairs:
echo item, " at index ", index
break: Early loop termination
continue: Early loop re-cycle
block label :
label is optional
For
block myblock:
echo "entering block"
while true:
echo "looping"
break # leaves the loop, but not the block
echo "still in block"
echo "outside the block"
try: - guarded block
except type : - exception clause
finally: - always executed
expression, yields value
raise object
For
var f: File
if open(f, "quote.txt"):
try:
echo readLine(f)
except IOError:
echo "IO error!"
except CatchableError:
echo "Unknown exception!"
# reraise the unknown exception:
raise
except: # catch all other exceptions
echo "Other error!"
finally:
close(f)
defer: statement
establishes implicit try/finally
statements following the defer are in the try block
the statement is in the finally block
procedures must be defined (or forward-declared) before it can be used
proc name ( parameter-list ): type = block
if name is suffixed by *, it is exported for use by other modules; otherwise, private
parameters immutable within procedure body; declare mutability using var in parameter list
parameters can have default values; parameter types can be inferred from defaults
procedures can be overloaded by number, types, and names of parameters
procedures can be nested (proc within a proc)
Procedures
proc fibonacci(n: int): int =
if n < 2:
return n
else:
return fibonacci(n - 1) + (n - 2).fibonacci
proc fibo2(n: int): int =
proc innerfibo(n:int): int = innerfibo(n-1) + innerfibo(n-2)
result = if n < 2: n else: innerfibo(n)
#echo innerfibo(5) # Error: undeclared identifier 'innerfibo'
Multiple calling syntax styles
positional call style 1: foo(a, b)
positional call style 2: a.foo(b)
call with named and positional arguments
call with named arguments (order is not relevant)
call as a command statement (no parenthesis needed)
Return values must be used or discarded
or procedure can be annotated with {. discardable .} pragma
Procedure call syntax
var f1 = fibonacci(5)
var f2 = 5.fibonacci()
echo "f1 == f2: ", f1 == f2
proc foo(a: string, b: string) =
echo "foo ", a, " and ", b
var a = "hello"
var b = "world"
foo(a,b)
a.foo(b)
Procedure call syntax
# Forward declaration
proc callme(x, y: int, s: string = "", c: char, b: bool = false)
# call with positional arguments # parameter bindings:
callme(0, 1, "abc", '\t', true) # (x=0, y=1, s="abc", c='\t', b=true)
# call with named and positional arguments:
callme(y=1, x=0, "def", '\t') # (x=0, y=1, s="def", c='\t', b=false)
# call with named arguments (order is not relevant):
callme(c='\t', y=1, x=0) # (x=0, y=1, s="", c='\t', b=false)
# call as a command statement: no () needed:
callme 0, 1, "ghi", '\t' # (x=0, y=1, s="ghi", c='\t', b=false)
any procedure that returns a value has an implicit result variable
result value is always returned automatically (at the exit) if no return is used
return types can also be inferred via auto declaration
Return values and result
proc yes(question: string): bool =
echo question, " (y/n)"
var answered = false
while answered != true:
case readLine(stdin)
of "y", "Y", "yes", "Yes": answered = true; result = true
of "n", "N", "no", "No": answered = true; result = false
else: echo "Please be clear: yes or no"
if yes("Should I delete all your important files?"):
echo "I'm sorry Dave, I'm afraid I can't do that."
else:
echo "I think you know what the problem is just as well as I do."
proc returnsInt(): auto = 1984
force compiler-verified no-side-effect checking by use of noSideEffect pragma
can also use the func keyword in place of proc; func x == proc x {. noSideEffect .}
symbols to be used take the name position
must be backtick-surrounded
paramters determine "ix"iness:
one paramter, prefix notation
two parameters, infix notation
no way to declare postfix operators
any operator can be called like an ordinary proc using the backticked notation
Operators
# The "UFO" operator, doing all the comparisons at once
proc `<==>`(lhs, rhs: int): int =
if lhs < rhs:
result = -1
elif lhs > rhs:
result = 1
else:
result = 0
echo 5 <==> 6
echo 7 <==> 6
echo 6 <==> 6
echo `<==>`(4, 5)
two syntaxes
"proc" syntax (regular procedure syntax, just no name)
"do" notation: do ( parameter-list ) -> type : expression
third syntax: -> and => from stdlib.sugar, using macros
Anonymous procedures
import sequtils
let powersOfTwo = @[1, 2, 4, 8, 16, 32, 64, 128, 256]
echo(powersOfTwo.filter do (x: int) -> bool: x > 32)
echo powersOfTwo.filter(proc (x: int): bool = x > 32)
proc greaterThan32(x: int): bool = x > 32
echo powersOfTwo.filter(greaterThan32)
proc forEach(str: string, c: proc (x: int): int ) =
for ch in str:
echo c(ord(ch))
stdlib.sugar macros
import sugar
# sugar provides a "->" macro that simplifies writing type declarations
# e.x. (char) -> char instead of
proc map(str: string, fun: (char) -> char): string =
for c in str:
result &= fun(c)
# sugar also provides a "=>" macro for the actual lambda value
echo "foo".map((c) => char(ord(c) + 1))
# the following code is exactly equivalent:
echo "foo".map(proc (c: char): char = char(ord(c) + 1))
Nim supports several
nimcall: the default for a Nim proc; same as fastcall for C compilers that support it
closure: default for a procedural type; hidden implicit parameter (environment)
cdecl: ditto to __cdecl inline`: inline the code directly in the caller; may be ignored
noinline: never inline
noconv: no convention at all
Nim supports several more...
fastcall: whatever this means to the C compiler
stdcall: ditto to Microsoft __stdcall
safecall: Microsoft's __safecall (all hardware regs pushed to stack)
thiscall: Microsoft C++ x86 class member function convention
syscall: C __syscall
these are all a part of the procedure's declaration type
Nim terminology alert
a symbol of kind: proc, func, method, iterator, macro, template, converter
syntax similar to tuples in many ways
provides inheritance
provides encapsulation from other modules
type-knowledge at runtime (of operator)
type name = object
followed by field declarations (name : type pairs), indented
field-names suffixed by * are exported (accessible to other modules)
type-name suffixed by * is exported
object types are always nominal types (not structural)
no base type means the class is implicitly final
each object type has a synthesized initializing constructor
takes all fields by name
defaults each field to the default value for that type
no other constructors are synthesized
Basic object syntax
type Person = object
first_name: string
last_name: string
age: int
var ted = Person(first_name: "Ted", last_name: "Neward", age: 55)
echo ted # "(first_name: "Ted", last_name: "Neward", age: 55)"
var bono: ref Person
new(bono)
bono.first_name = "Bono"
echo bono[]
no need for special "getter" syntax, just write a proc
"setters", however, need to get fancy
Properties
# Person fields are all inaccessible due to non-exported syntax
proc firstName*(p: Person): string = p.first_name
proc lastName*(p: Person): string = p.last_name
proc age*(p: Person): int = p.age
echo ted.firstName() # or ted.firstName would work
proc `firstName=`*(p: var Person, newName: string) = p.first_name = newName
proc `lastName=`*(p: var Person, newName: string) = p.last_name = newName
#proc `age=`*(p: var Person, newAge: int) = p.age = age # This causes problems
ted.firstName = "Theodore"
echo ted
type name = ref object of base-type
for runtime type information, inherit from RootObj
Inheritance
... are just procedures taking the object type as the first parameter
recall that foo(obj, args) is syntactically equivalent to obj.foo(args)
however:
proc is always static-dispatch
to achieve dynamic dispatch, use method instead of proc
Dynamic dispatch
type
Expression = ref object of RootObj ## abstract base class for expressions
Literal = ref object of Expression
x: int
PlusExpr = ref object of Expression
a, b: Expression
proc newLit(x: int): Literal = Literal(x: x)
proc newPlus(a, b: Expression): PlusExpr = PlusExpr(a: a, b: b)
method eval(e: Expression): int {.base.} = quit "E_NOTIMPL"
method eval(e: Literal): int = e.x
method eval(e: PlusExpr): int = eval(e.a) + eval(e.b)
echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
essentially an object type containing named fields of procedures
this covers what other languages call interfaces, protocols, abstract, etc
Interfaces
type IntFieldInterface = object
getter: proc (): int
setter: proc (x: int)
proc intField(init = 0): IntFieldInterface =
var captureMe = init
proc getter(): int = result = captureMe
proc setter(x: int) = captureMe = x
result = IntFieldInterface(getter: getter, setter: setter)
var anInt = intField(12)
echo anInt.getter()
anInt.setter(34)
echo anInt.getter()
modules are units of encapsulation and compilation
each file is its own module
module names must be unique in a project
module name can be used as an identifier for the module itself
each module's top-level statements execute at program start
isMainModule magic constant signals current module is "main"
modules can reference symbols of other modules
only exported (*-suffixed) symbols are available for reference
names can be module-qualified (must be if ambiguous)
importing symbols
import module-name: import all exported symbols
import module-name except symbol: exclude symbol in import
from module-name import symbol-list: import specific symbols
from module-name import nil: force all symbols to be qualified
include file-path-list: include file's/files' contents as module
effectively ignores module boundaries
filesystem
networking
peripherals
graphics
sound
...
FFI: foreign function interface
The interface by which control transitions between VM-controlled code and "native" code outside the VM's sight or control
hosted code "calling out" to native code
native code "calling in" to hosted code
code to boostrap the VM into existence
Any FFI requires platform knowledge
code compilation & linking
code resolution & loading
function resolution by name
calling conventions
memory management (heap allocation/deallocation)
platform-level debugging skills
... whatever the platform
"native" FFI
C
C++
Obj-C
JavaScript FFI
"native" and JS FFI are mutually exclusive
calling C libraries
C-calling-Nim
static
library code embedded in calling program code
reduces startup time/cost
single executable file
dynamic
libraries reside in their own executable files
discovered at runtime
linked at runtime by name
Nim prefers dynamic, generally
pointer, csize
cint, cuint, cshort, cushort
clong, clonglong, culong, culonglong
cfloat, cdouble, clongdouble
cchar, cschar, cuchar
cstring, cstringArray
create a Nim-declared procedure declaration "wrapper"
make sure to use Nim c types to match up with C types
annotate with appropriate pragmas
importc (optionally passing in exported name, if different)
header: C-header-file-name
varargs (if necessary)
discardable (if return can be thrown away)
call as normal
Nim will do much of the type coercion/conversion
Simple C FFI
proc puts(str: cstring) {. importc, header: "stdio.h" .}
puts("Howdy world")
proc printf(format: cstring): cint {.importc, varargs, header: "stdio.h".}
discard printf("My name is %s and I am %d years old!\n", "Ted", 50)
create a Nim-declared type "wrapper"
define the type using c types to match the C declaration
annotate with the appropriate pragmas
importc: struct C-struct-name
header: C-header-file-name
use as normal
Simple C types FFI
type
CTime = int64
proc time(arg: ptr CTime): CTime {.importc, header: "<time.h>".}
type
TM {.importc: "struct tm", header: "<time.h>".} = object
tm_min: cint
tm_hour: cint
proc localtime(time: ptr CTime): ptr TM {.importc, header: "<time.h>".} #1
var seconds = time(nil) #2
let tm = localtime(addr seconds) #3
echo(tm.tm_hour, ":", tm.tm_min) #4
... a pretty nice language
... easily applicable to both systems and web development
... syntactically a blend of C/C++ and Python
... conceptually relatively easy to follow
Nim manual
https://nim-lang.org/docs/manual.html
Nim basics tutorial
https://narimiran.github.io/nim-basics/
Nim tutorial
https://nim-lang.org/docs/tut1.html
https://nim-lang.org/docs/tut2.html (Advanced constructs)
https://nim-lang.org/docs/tut3.html (Macros)
Nim backends
https://nim-lang.org/docs/backends.html
NimScript
https://nim-lang.org/docs/nims.html
Mastering Nim, Rumpf
https://www.amazon.com/Mastering-Nim-complete-programming-language-ebook/dp/B0CGGHZ4HP/ref=tmm_kin_swatch_0
Nim Programming Language Made Easy, Smith
https://www.amazon.com/Programming-Language-Made-Easy-Comprehensive-ebook/dp/B0DKB3564J/ref=sr_1_4
Architect, Engineering Manager/Leader, "force multiplier"
http://www.newardassociates.com
http://blogs.newardassociates.com
Books
Developer Relations Activity Patterns (w/Woodruff, et al; APress, 2026)
Professional F# 2.0 (w/Erickson, et al; Wrox, 2010)
Effective Enterprise Java (Addison-Wesley, 2004)
SSCLI Essentials (w/Stutz, et al; OReilly, 2003)
Server-Based Java Programming (Manning, 2000)
value propagating upwards through the stack
value can be intercepted, stopping propagation
failure to intercept eventually terminates thread/process
raise value : begins propagating expression value
raise : re-raises existing exception value
typically exception types...
extend system.Exception
suffixed with "Error"
try: : sets up guarded block
except exception-type : : block handler
finally: : always-executed block
system module
getCurrentException()
getCurrentExceptionMsg()
{. raises [ error-type-list ] .}
compiler will verify exception types during compile-time