⚡ Nim Philosophy & Introduction

🎯 What is Nim?

Nim is a statically-typed, compiled systems programming language that combines Python-like syntax with C-level performance. Created by Andreas Rumpf, Nim features metaprogramming via macros, compile-time evaluation, and zero-overhead abstractions. It compiles to C, C++, or JavaScript, offering cross-platform deployment with excellent performance.

🔬 Core Pillars

  • Python-like Syntax: Readable, expressive, indentation-based.
  • Statically Typed: Type inference, generics, and ADTs.
  • Metaprogramming: AST-based macros, templates, compile-time execution.
  • Memory Management: Optional GC, ARC, or manual memory.
  • Zero-overhead C FFI: Direct C library integration without wrappers.
# Nim: The perfect blend of Python and C echo "Hello, Nim!" # Type inference let answer = 42 var name = "Nim" # Compile-time evaluation const version = staticExec("nim -v") echo version

📦 Variables & Type System

🎯 Variable Declarations

let for immutable bindings, var for mutable. const for compile-time constants. Nim uses type inference but supports explicit types. Primitive types: int, float, bool, char, string.

🏷️ Advanced Types

  • tuple: Heterogeneous fixed-length collections
  • enum: Named constants
  • range: Subrange types (e.g., 0..100)
  • distinct: Strong type aliases
# Variable declarations let pi = 3.14159 # immutable var counter = 0 # mutable counter += 1 # Explicit typing var age: int = 25 var name: string = "Nim" # Distinct type (strong alias) type UserId = distinct int var id = UserId(12345) # Ranges and enums type Day = enum Monday, Tuesday, Wednesday var score: range[0..100] = 95 # Tuples let person = (name: "Alice", age: 30) echo person.name

⚙️ Procedures & Methods

🎯 Functions as First-Class

proc defines procedures. func ensures no side effects. method for dynamic dispatch. Default parameters, var parameters, and return types.

# Basic procedure proc add(x, y: int): int = return x + y # Implicit return (last expression) proc multiply(a, b: int): int = a * b # Default parameters proc greet(name: string = "World"): string = "Hello, " & name # Var parameters (mutable) proc increment(x: var int) = x += 1 var value = 10 increment(value) echo value # 11 # Function with no side effects func square(x: int): int = x * x # Anonymous procedures let double = proc(x: int): int = x * 2 echo double(5)

🔀 Control Flow Mastery

🎯 Conditional Execution

if/elif/else, case statements (pattern matching), when for compile-time branching.

# If statement let score = 85 if score >= 90: echo "A+" elif score >= 80: echo "B" else: echo "Needs improvement" # Case statement (pattern matching) let day = "Monday" case day of "Monday", "Tuesday", "Wednesday": echo "Weekday" of "Saturday", "Sunday": echo "Weekend" else: echo "Invalid" # Compile-time when when defined(windows): echo "Windows platform" elif defined(linux): echo "Linux platform" # Ternary-like expression let status = if age >= 18: "adult" else: "minor"

🔄 Iterators & Loops

🎯 Iteration Constructs

for loops with ranges, iterators, and custom iterators. while loops. countup, countdown, and infinite iterators.

# Range iteration for i in 1..5: echo i # Countup and countdown for i in countup(0, 10, 2): echo i # 0,2,4,6,8,10 for i in countdown(10, 0): echo i # Iterate over collections let fruits = @["apple", "banana", "cherry"] for fruit in fruits: echo fruit # With index for index, fruit in fruits: echo index, ": ", fruit # Custom iterator iterator countTo(n: int): int = var i = 0 while i < n: yield i inc i for x in countTo(5): echo x

📊 Sequences & Arrays

🎯 Dynamic and Static Collections

seq (dynamic arrays), array (fixed-size), openArray (parameter type). Strings are sequences of characters.

# Sequences (dynamic) var numbers: seq[int] = @[1, 2, 3, 4, 5] numbers.add(6) numbers.insert(0, 0) echo numbers # @[0,1,2,3,4,5,6] # Array (fixed size) var fixed: array[5, int] = [10, 20, 30, 40, 50] fixed[2] = 35 # OpenArray (for procedure parameters) proc sum(arr: openArray[int]): int = result = 0 for x in arr: result += x echo sum(numbers) # Works with both seq and array # String operations let text = "Nim programming" echo text.len echo text[0..2] # "Nim" echo text.split(' ')

🏗️ Objects & Inheritance

🎯 Object-Oriented Programming

object types with inheritance (ref object for reference types). method for dynamic dispatch, property for getters/setters.

# Base object type Animal = ref object of RootObj name: string age: int Dog = ref object of Animal breed: string # Constructor proc newAnimal(name: string, age: int): Animal = Animal(name: name, age: age) # Method (dynamic dispatch) method speak(a: Animal): string {.base.} = "Some sound" method speak(d: Dog): string = "Woof! Woof!" # Usage let animal = newAnimal("Generic", 5) let dog = Dog(name: "Rex", age: 3, breed: "German Shepherd") echo animal.speak() # "Some sound" echo dog.speak() # "Woof! Woof!" # Object variants (tagged unions) type ShapeKind = enum skCircle, skRectangle Shape = object case kind: ShapeKind of skCircle: radius: float of skRectangle: width, height: float

🔧 Generics & Templates

🎯 Type-Parametric Programming

Generics for type-safe polymorphism. templates for code substitution (like C macros but hygienic).

# Generic procedure proc max[T](a, b: T): T = if a > b: a else: b echo max(10, 20) # 20 echo max(3.14, 2.71) # 3.14 # Generic type type Stack[T] = object data: seq[T] proc push[T](s: var Stack[T], value: T) = s.data.add(value) proc pop[T](s: var Stack[T]): T = s.data.pop() # Templates (hygienic macros) template times(n: int, body: untyped) = for i in 1..n: body times(3, echo "Hello") # Compile-time evaluation proc factorial(n: static int): int = result = 1 for i in 1..n: result *= i const fact5 = factorial(5) # Computed at compile time

✨ Macros & Metaprogramming

🎯 AST Manipulation

Macros operate on Nim's AST at compile time, enabling DSL creation, code generation, and advanced optimizations.

import macros # Simple macro macro debug(expr: untyped): untyped = result = quote do: echo `expr`, " = ", `expr` var x = 42 debug(x) # Output: x = 42 # Macro for logging macro log(msg: string): untyped = echo "Compile-time: ", msg result = newCall(bindSym"echo", newLit("Runtime: " & msg)) log("Nim macros are powerful!") # Custom assert with message macro myAssert(cond: untyped, msg: string): untyped = result = quote do: if not `cond`: raise newException(AssertionDefect, `msg`) myAssert(1 + 1 == 2, "Math is broken")

🛡️ Error Handling

🎯 Exceptions & Option Types

Nim uses exceptions for error handling with try/except/finally. Also supports Option[T] for nullable values.

# Exception handling proc riskyOperation(x: int): int = if x == 0: raise newException(ValueError, "Cannot be zero") return 100 div x try: echo riskyOperation(0) except ValueError as e: echo "Caught: ", e.msg finally: echo "Cleanup" # Option type import options proc safeDivide(a, b: int): Option[int] = if b == 0: return none(int) else: return some(a div b) let result = safeDivide(10, 2) if result.isSome: echo result.get() else: echo "Division by zero" # Defer statement (automatic cleanup) proc readFile(filename: string) = var f = open(filename) defer: f.close() # File will be closed automatically echo f.readAll()

⏩ Concurrency (async/threads)

🎯 Parallel & Concurrent Programming

Nim supports threads (OS threads), async/await for async I/O, and channels for communication.

import threadpool, asyncdispatch, asyncnet # Thread pool example proc worker(id: int): int = echo "Thread ", id, " working" result = id * id let results = parallel: for i in 1..4: spawn worker(i) echo results # Async I/O proc serveClient(client: AsyncSocket) {.async.} = await client.send("Hello from Nim!\n") client.close() proc startServer() {.async.} = let server = newAsyncSocket() server.bindAddr(port=8080.Port) server.listen() echo "Server running on port 8080" while true: let client = await server.accept() asyncCheck serveClient(client) # asyncCheck startServer() # runForever() # Channels for communication import channels var chan: Channel[int] chan.open() chan.send(42) let value = chan.recv()

💾 Memory Management

🎯 GC, ARC, and Manual Memory

Nim offers multiple memory management strategies: --gc:refc (default), --gc:arc (automatic reference counting), --gc:orc (with cycle detection), or --gc:none for manual control.

# Ref objects (garbage collected) type Node = ref object value: int next: Node var head: Node head = Node(value: 1) head.next = Node(value: 2) # Manual memory with --gc:none {.experimental.} proc allocate(size: int): ptr byte = cast[ptr byte](c_malloc(size)) proc deallocate(p: ptr byte) = c_free(p) # Move semantics (ARC) var a = @[1, 2, 3] var b = a # Moves ownership (ARC) # a is now empty # Destructors type Resource = object data: ptr int proc `=destroy`(x: var Resource) = if x.data != nil: deallocate(x.data) x.data = nil

🔌 FFI & C Interop

🎯 Seamless C Integration

Nim can call C libraries directly without wrappers. {.importc.} pragma imports C functions, {.header.} specifies includes.

# Import C standard library proc printf(format: cstring): int {.importc: "printf", varargs, header: "".} printf("Hello from C! %d\n", 42) # Import custom C function {.passC: "-lm".} proc sin(x: float64): float64 {.importc: "sin", header: "".} echo sin(3.14159 / 2) # Define C-compatible structs type Point {.bycopy.} = object x, y: cint proc distance(p1, p2: Point): cfloat {.importc: "distance", header: "geometry.h".} # Inline C code {.emit: """ static int add_c(int a, int b) { return a + b; } """.} proc addFromC(a, b: cint): cint {.importc: "add_c", nodecl.} echo addFromC(10, 20)

📦 Modules & Package Manager

🎯 Nimble Package Manager

Nim uses Nimble for dependency management. Modules are imported with import, and you can create your own libraries.

# Importing modules import os, strutils, math echo getCurrentDir() echo " hello world ".strip().toUpperAscii() echo sqrt(144.0) # Selective imports import std/[json, sequtils, sugar] # Exporting modules (mymodule.nim) # module mymodule # export myFunction # Nimble package example (nimble.nimble file) # [Package] # name = "mypackage" # version = "0.1.0" # author = "You" # description = "My awesome package" # license = "MIT" # srcDir = "src" # # [Deps] # requires "nim >= 1.6.0" # Create your own module # in file: myutils.nim proc greet*(name: string): string = "Hello, " & name # Import custom module import myutils echo greet("Nim")