⚡ 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")