Nim Interview Questions with Answers
Most Asked Nim Interview Questions for Software Engineer Roles
Introduction
This page provides a complete collection of Nim Interview Questions and Answers designed for software developers, system programmers, competitive programmers, and candidates preparing for programming language interviews. Nim is a modern, statically typed compiled programming language designed for performance, efficiency, and developer productivity. It combines features from multiple programming paradigms including procedural, object-oriented, and functional programming. This interview guide covers beginner, intermediate, and advanced Nim concepts including syntax, variables, data types, procedures, generics, macros, memory management, object-oriented programming, concurrency, compilation, and real-world programming scenarios.
Why Nim?
- High performance – compiles to C, C++, and JavaScript with zero-cost abstractions
- Powerful macro system – metaprogramming capabilities for code generation and DSLs
- Efficient memory management – manual, garbage collection, or reference counting options
- Multi‑paradigm – supports procedural, OOP, functional, and concurrent programming
- Generics and type inference – write concise and type‑safe code
- Cross‑platform – compiles to numerous platforms and backends
- Growing community – active development and modern language features
Most Asked Nim Interview Questions
Nim is a statically typed, compiled programming language that combines performance with expressive syntax. It features a powerful macro system, multiple memory management options, and compiles to C, C++, and JavaScript.
- Statically typed – type checking at compile time
- Compiled – to efficient native code
- Macros – advanced metaprogramming
- Garbage collected (or manual via
--gc:arc) - Multi‑paradigm – procedural, OOP, functional
# Hello World in Nim
echo "Hello, World!"
# Using a procedure
proc greet(): string =
return "Hello, World!"
echo greet()Nim can be installed via package managers, from source, or using choosenim (version manager).
- choosenim:
curl https://nim-lang.org/choosenim/init.sh -sSf | sh - Homebrew (macOS):
brew install nim - Linux (apt):
sudo apt install nim - Windows: download installer from nim-lang.org
- Build from source: clone and run
make
# Variables in Nim
var mutable_var = "Hello" # Mutable variable
let immutable_var = "World" # Immutable binding
const CONSTANT = "I am constant" # Compile-time constant
# Type inference
var inferred = 42
# Multiple assignment (not directly, but via tuples)
var (a, b, c) = (1, 2, 3)
# Display
echo mutable_var
echo immutable_var
echo inferred
echo CONSTANTNim uses var for mutable variables, let for immutable bindings, and const for compile‑time constants.
- var:
var x = 10(mutable) - let:
let y = 20(immutable) - const:
const PI = 3.14(compile‑time) - Type inference: types are optional
- Explicit types:
var name: string = "Nim"
# Data Types in Nim
# Numeric types
var int_num: int = 10
var float_num: float = 3.14
# Boolean
var is_active: bool = true
var is_inactive: bool = false
# Character and string
var char: char = 'A'
var str_val: string = "Hello Nim"
# Sequences (dynamic arrays)
var seq_val: seq[int] = @[1, 2, 3, 4, 5]
# Tuples (fixed-size, heterogeneous)
var tuple_val: tuple[a: int, b: string] = (1, "hello")
# Tables (hash maps)
import tables
var table_val = {"name": "Alice", "age": "25"}.toTable
# Sets
var set_val: set[char] = {'a', 'b', 'c'}
# None (nil for ref types)
var none_val: ref int = nil
# Type checking
echo typeof(int_num) # int
echo str_val is string # trueNim provides a rich set of built‑in types:
- Integers:
int,int8,int16,int32,int64,uint - Floats:
float,float32,float64 - Characters:
char - Strings:
string - Booleans:
bool - Containers:
seq,array,tuple,set,table - Objects: custom types
# Procedures (Functions) in Nim
# Basic procedure
proc add(a, b: int): int =
return a + b
# Single-expression procedure (implicit return)
proc subtract(a, b: int): int = a - b
# Default parameters
proc greet(name: string = "Guest"): string =
return "Hello, " & name & "!"
# Procedure with multiple return values (using tuple)
proc divide(a, b: int): (int, int) =
return (a div b, a mod b)
# Higher-order procedure
proc operate(a, b: int, operation: proc (x, y: int): int): int =
return operation(a, b)
# Lambda (anonymous procedure)
let multiply = proc(a, b: int): int = a * b
# Usage
echo add(5, 3)
echo subtract(10, 4)
echo greet("Alice")
var (quotient, remainder) = divide(10, 3)
echo quotient, " ", remainder
echo operate(6, 7, multiply)Procedures (functions) are defined with the proc keyword. They can have parameters, return types, and default values.
- Basic:
proc add(x, y: int): int = x + y - Default parameters:
proc greet(name: string = "Guest") - Variable arguments:
proc sum(nums: varargs[int]): int - Return:
return valueor implicit last expression - Lambda:
let f = proc(x: int): int = x * 2
# Sequences (like Python lists)
# Sequence creation
var numbers = @[1, 2, 3, 4, 5]
var strings = @["Apple", "Banana", "Orange"]
var mixed: seq[SomeNumber] # Not directly, use object variants
# Access and modify
echo numbers[2] # Access element
numbers[2] = 10 # Modify element
# Sequence operations
echo numbers.len
numbers.add(6) # Add element
numbers.delete(numbers.len - 1) # Remove last
# Iteration
for num in numbers:
echo num
# Sequence comprehension (using map from sequtils)
import sequtils
let doubled = numbers.map(proc(x: int): int = x * 2)
let filtered = numbers.filter(proc(x: int): bool = x > 2)
# Sum
let sum_val = numbers.foldl(a + b)
# Display
echo doubled
echo filtered
echo sum_valSequences (seq) are dynamic arrays that can grow or shrink. They are the most commonly used container.
- Creation:
var nums = @[1, 2, 3] - Access:
nums[0] - Add:
nums.add(4) - Insert:
nums.insert(5, 1) - Delete:
nums.delete(1)
# Collections in Nim
# Sequence (mutable, dynamic)
var mutable_seq = @[1, 2, 3]
mutable_seq.add(4)
mutable_seq.delete(1)
# Array (fixed-size, stack-allocated)
var immutable_arr: array[3, int] = [1, 2, 3]
# Tuple (immutable, fixed-size)
var tuple_val = (1, "hello")
# Set (unique, unordered)
var mutable_set: set[char] = {'a', 'b', 'c'}
mutable_set.incl('d') # add
mutable_set.excl('b') # remove
# Table (key-value pairs)
import tables
var mutable_table = {"key1": "value1"}.toTable
mutable_table["key2"] = "value2"
mutable_table.del("key1")
# Operations
var numbers = @[1, 2, 3, 4, 5, 6]
let evens = numbers.filter(proc(x: int): bool = x mod 2 == 0)
let doubled = numbers.map(proc(x: int): int = x * 2)
let sum_val = numbers.foldl(a + b)
let exists = numbers.any(proc(x: int): bool = x > 10)
let all_even = numbers.all(proc(x: int): bool = x mod 2 == 0)
echo evens
echo doubled
echo sum_valTuples are fixed‑size heterogeneous containers. They can be accessed by index or by field name if named.
- Anonymous:
(1, "hello") - Named:
(x: 10, y: 20) - Access:
t[0]ort.x - Assignment:
var (a, b) = (1, 2) - Unpacking:
let (x, y) = getPoint()
# Objects (like Python dataclasses)
type
Person = object
name: string
age: int
city: string
# Constructor (default values)
proc newPerson(name: string, age: int, city: string = "Unknown"): Person =
Person(name: name, age: age, city: city)
# Usage
var person1 = newPerson("Alice", 25, "NYC")
var person2 = newPerson("Bob", 30, "LA")
# Copy (using assignment, it's by value)
var person3 = person1
person3.age = 26
# Access
echo person1.name
echo person1.age
echo person1.city
# Display (requires custom $ operator)
proc `$`(p: Person): string =
return "Person(name: " & p.name & ", age: " & $p.age & ", city: " & p.city & ")"
echo person1Arrays have a fixed size known at compile time. They are allocated on the stack and are more performant than sequences.
- Declaration:
var arr: array[3, int] - Initialization:
var arr = [1, 2, 3] - Access:
arr[0] - Length:
arr.len - Multi‑dimensional:
array[2, array[3, int]]
# Sealed classes (using object variants)
type
ResultKind = enum
rkSuccess, rkError, rkLoading
Result = object
case kind: ResultKind
of rkSuccess:
data: string
of rkError:
message: string
of rkLoading:
discard
proc handleResult(r: Result): string =
case r.kind
of rkSuccess:
return "Success: " & r.data
of rkError:
return "Error: " & r.message
of rkLoading:
return "Loading..."
# Usage
let success = Result(kind: rkSuccess, data: "Data loaded")
echo handleResult(success)
# Alternative using inheritance (methods)
type
Shape = object of RootObj
Circle = object of Shape
radius: float
Rectangle = object of Shape
width, height: float
Point = object of Shape
method area(s: Shape): float = 0.0
method area(c: Circle): float = 3.14159 * c.radius * c.radius
method area(r: Rectangle): float = r.width * r.height
let circle = Circle(radius: 5.0)
echo circle.area()Sets are unordered collections of distinct elements. They are efficient for membership tests.
- Declaration:
var s: set[char] - Initialization:
var s = {'a', 'b', 'c'} - Add:
s.incl('d') - Remove:
s.excl('a') - Membership:
'b' in s
# Null safety in Nim (using Option and Result)
import std/options
import std/result
# Option type
var nullable_string: Option[string] = none(string)
var non_nullable_string: string = "Hello"
# Safe access with if
if nullable_string.isSome:
echo "String is: ", nullable_string.get()
echo "Length: ", nullable_string.get().len
# Elvis operator equivalent (using get)
let value = nullable_string.get("default")
# Safe navigation using get with default
type Person = object
name: string
var person = Person(name: "Alice")
# Using get with default for missing fields (not applicable in Nim, but we can use a helper)
proc getAttr[T](obj: T, field: string): string = "" # Not idiomatic
# Using Result for safe operations
proc safeLength(s: Option[string]): Result[int, string] =
if s.isSome:
ok(s.get().len)
else:
err("No string")
# Usage
echo value
echo safeLength(some("hello")).get(0)Tables are associative arrays (hash maps) mapping keys to values.
- Creation:
var t = {1: "one", 2: "two"}.toTable - Access:
t[1] - Add/Update:
t[3] = "three" - Contains:
t.hasKey(2) - Iteration:
for k, v in t: echo k, " ", v
# Control Flow in Nim
# If-else
var age = 25
var status: string
if age < 18:
status = "Minor"
else:
status = "Adult"
echo status
# If-elif-else
var grade = 'A'
var result: string
if grade == 'A':
result = "Excellent"
elif grade == 'B':
result = "Good"
elif grade == 'C':
result = "Fair"
else:
result = "Needs Improvement"
echo result
# For loop
for i in 0..4:
echo i
# For loop with step (using countup with step)
for i in countup(1, 9, 2):
echo i
# For loop descending
for i in countdown(10, 1):
echo i
# While loop
var i = 0
while i < 5:
echo i
inc(i)
# Do-while (using while with break)
i = 0
while true:
echo i
dec(i)
if i <= 0:
break
# For loop with items and pairs
var items = @['a', 'b', 'c']
for index, item in items:
echo index, ": ", itemNim supports if, elif, and else with an optional then keyword (implicit).
- Syntax:
if condition: ... elif condition: ... else: ... - No parentheses required
- Indentation sensitive (like Python)
- If expression:
let x = if a > b: a else: b
# Classes (Objects and Inheritance) in Nim
# Base class (object of RootObj)
type
Animal = object of RootObj
name: string
Dog = object of Animal
breed: string
# Methods (dynamic dispatch)
method makeSound(a: Animal): string =
return "Animal sound"
method makeSound(d: Dog): string =
return "Woof!"
# Abstract classes (using methods without implementation)
type
Vehicle = object of RootObj
method start(v: Vehicle) {.base.} = discard
method stop(v: Vehicle): string = "Stopped"
# Interface-like (using concepts or methods)
type
Flyable = concept x
x.fly() is string
x.land() is string
# Multiple inheritance is not supported, but composition is used.
# Usage
var dog = Dog(name: "Rex", breed: "German Shepherd")
echo dog.makeSound()
echo dog.namefor loops iterate over ranges, sequences, or any iterable.
- Range:
for i in 0..5: echo i - Exclusive range:
for i in 0..<5: echo i - Iterating over containers:
for item in seq: echo item - Countdown:
for i in countdown(5, 0): echo i
# Properties in Nim (using getter/setter procs)
type
Person = object
name: string
age: int
email: string
proc getName(p: Person): string = p.name
proc setName(p: var Person, value: string) = p.name = value.strip()
proc getAge(p: Person): int = p.age
proc setAge(p: var Person, value: int) =
if value >= 0: p.age = value
proc getEmail(p: Person): string = p.email
proc setEmail(p: var Person, value: string) = p.email = value
# Computed property
proc fullName(p: Person): string =
return p.name & " (Age: " & $p.age & ")"
# Lazy property (using a ref or custom type)
type
Lazy[T] = object
value: T
computed: bool
proc getLazy[T](l: var Lazy[T], compute: proc(): T): T =
if not l.computed:
l.value = compute()
l.computed = true
l.value
# Usage
var person = Person(name: " Alice ", age: 25)
setName(person, "Bob")
echo person.name
setAge(person, 26)
echo person.age
echo fullName(person)while loops execute as long as a condition holds.
- Syntax:
while condition: ... - Break:
breakexits the loop - Continue:
continueskips to next iteration
# Class Methods and Static Methods
type
MyClass = object
# Class variable (using a global var)
var counter: int = 0
proc newMyClass(): MyClass =
inc(counter)
result = MyClass()
proc instanceMethod(self: MyClass): string =
return "Instance method called"
# Class method (using a proc that takes a type)
proc classMethod(cls: typedesc[MyClass]): string =
return "Class method called, counter: " & $counter
# Static method (just a proc in the module)
proc staticMethod(): string =
return "Static method called"
# Factory method (using a proc that returns instance)
proc createMyClass(): MyClass =
return newMyClass()
# Usage
echo staticMethod()
var obj1 = newMyClass()
var obj2 = createMyClass()
echo counter
echo MyClass.classMethod()
# Class variable alternative
type
AnotherClass = object
var instances: seq[AnotherClass] = @[]
proc newAnotherClass(): AnotherClass =
instances.add(result)
result = AnotherClass()
proc getInstances(): seq[AnotherClass] = instancescase is Nim’s pattern matching construct (similar to switch).
- Syntax:
case expr: of pattern: ... else: ... - Range matching:
of 0..9: echo "digit" - Multiple values:
of 'a', 'e', 'i': echo "vowel" - Else: mandatory unless exhaustive
# Exception Handling in Nim
# Try-except block
proc divide(a, b: int): int =
try:
return a div b
except DivByZeroError:
echo "Division by zero!"
return 0
# Try-except with specific exceptions
proc safeDivide(a, b: int): string =
try:
return $ (a div b)
except DivByZeroError:
return "Division by zero"
except ValueError:
return "Invalid value"
except:
return "Error: " & getCurrentExceptionMsg()
# Custom exception
type
InvalidAgeError = object of Exception
proc validateAge(age: int) =
if age < 0 or age > 150:
raise newException(InvalidAgeError, "Invalid age")
# Finally block
proc readFile(): string =
try:
echo "Reading file..."
# File operations
return "File content"
except:
echo "Error reading file"
return ""
finally:
echo "Closing resources..."
# Context manager (using with)
import std/with
proc writeToFile() =
var f: File
with f = open("test.txt", fmWrite):
f.write("Hello Nim")
# Usage
echo divide(10, 2)
echo divide(10, 0)
try:
validateAge(200)
except InvalidAgeError:
echo "Invalid age"
echo readFile()
writeToFile()Objects are composite types that group fields. They support inheritance and can have methods.
- Definition:
type Person = object name: string; age: int - Instantiation:
var p = Person(name: "Alice", age: 30) - Field access:
p.name - Inheritance:
type Employee = object of Person
# Lambda Expressions (Anonymous Procedures)
# Basic lambda
let square = proc(x: int): int = x * x
# Lambda with multiple parameters
let doubled = proc(x: int): int = x * 2
# Higher-order procedures
proc performOperation(x, y: int, operation: proc(a, b: int): int): int =
return operation(x, y)
# Lambda with multiple lines (using block)
let complex = proc(x: int): int =
let y = x * 2
return y + 10
# Function reference
proc multiply(x, y: int): int = x * y
let multiplyRef = multiply
# Returning lambda from procedure
proc getOperation(opType: string): proc(a, b: int): int =
case opType
of "add": return proc(a, b: int): int = a + b
of "subtract": return proc(a, b: int): int = a - b
else: return proc(a, b: int): int = 0
# Usage
echo square(5)
echo performOperation(10, 20, proc(a, b: int): int = a * b)
let add = getOperation("add")
echo add(5, 3)
# Lambda with filter and map (using sequtils)
import sequtils
var numbers = @[1, 2, 3, 4, 5]
let filtered = numbers.filter(proc(x: int): bool = x > 2)
let mapped = numbers.map(proc(x: int): int = x * 2)
echo filtered
echo mappedMethods are procedures that are dispatched dynamically (like virtual functions). Use the method keyword.
- Method:
method speak(p: Person): string = "Hello" - Override:
method speak(e: Employee): string = "Hi from " & e.name - Multimethods: dynamic dispatch on multiple arguments
# Scope Functions (using templates and with)
# Nim doesn't have built-in scope functions like Kotlin, but we can use templates.
# let - execute block (using a template)
template letScope(body: untyped): untyped =
let x = body
x
# Usage
let result = letScope:
var y = 10
y * 2
echo result
# apply - configure object (using with)
type Person = object
name: string
age: int
proc updatePerson(p: var Person) =
p.age += 1
var person = Person(name: "Alice", age: 25)
person.updatePerson()
echo person.age
# also - perform additional operations (using a proc)
proc also[T](x: T, f: proc(x: var T)): T =
var y = x
f(y)
return y
let newPerson = also(person, proc(p: var Person) = p.age += 1)
echo newPerson.age
# take-if equivalent
proc takeIf[T](cond: proc(x: T): bool, value: T): Option[T] =
if cond(value): some(value) else: none(T)
let ageOk = takeIf(proc(x: int): bool = x >= 18, 25)
if ageOk.isSome:
echo "Valid age"- var: mutable variable, evaluated at runtime
- let: immutable variable, evaluated at runtime
- const: compile‑time constant, must be known at compile time
# Extension Functions in Nim
# Nim doesn't have extension functions directly, but we can use converters or wrapper procs.
# String extensions
proc isEmail(s: string): bool =
return '@' in s and '.' in s
proc addPrefix(s: string, prefix: string): string =
return prefix & s
# Numeric extensions
proc isEven(n: int): bool = n mod 2 == 0
proc isOdd(n: int): bool = n mod 2 != 0
# List extensions (using seq)
proc secondOrNone[T](s: seq[T]): Option[T] =
if s.len >= 2: some(s[1]) else: none(T)
# String word count
proc wordCount(s: string): int =
return s.splitWhitespace.len
# Monkey patching (using converters, not recommended)
# Instead, we can use a wrapper type
type
MyString = distinct string
proc isEmail(s: MyString): bool =
return '@' in string(s) and '.' in string(s)
# Usage
echo "test@example.com".isEmail()
echo addPrefix("Hello", "Greeting: ")
echo 5.isEven()
echo wordCount("Hello World")
echo secondOrNone(@[1, 2, 3]).get(0)Nim infers types from the initial value, so explicit annotations are often optional.
- Variable:
let x = 42→int - Procedure return: inferred from last expression
- Generic types: inferred from usage
# Type Aliases in Nim
# Type aliases using distinct types or simple type definitions
type
Operation = proc (a, b: int): int
UserMap = Table[string, Table[string, string]]
UserId = int
UserName = string
# Usage
proc add(a, b: int): int = a + b
proc multiply(a, b: int): int = a * b
proc execute(op: Operation, a, b: int): int =
return op(a, b)
# Complex type alias (tuple)
type
User = tuple[name: string, age: int] # (name, age)
var users: Table[string, User] # using table
users["user1"] = (name: "Alice", age: 25)
users["user2"] = (name: "Bob", age: 30)
# Function type alias
type StringPredicate = proc(s: string): bool
proc filterStrings(strings: seq[string], pred: StringPredicate): seq[string] =
result = @[]
for s in strings:
if pred(s):
result.add(s)
# Usage
echo execute(add, 5, 3)
echo execute(multiply, 5, 3)
echo users["user1"].nameUse import to bring symbols from other modules. You can also use from and export.
- Simple:
import math - Specific:
from math import sqrt, PI - Alias:
import times as t - Export:
export mymodule
# Inline Functions (using templates or macros)
# Nim doesn't have inline functions per se, but templates are similar.
# Timing decorator (using template)
import times
template measureTime(body: untyped): untyped =
let start = cpuTime()
body
let elapsed = cpuTime() - start
echo "Time: ", elapsed, "s"
# Usage
measureTime:
sleep(100)
echo "Done"
# Reified type parameter (using generic types)
proc isType[T](value: T, typ: typedesc): bool =
return value is typ
# Filter by type
proc filterByType[T](lst: seq[RootObj], typ: typedesc[T]): seq[T] =
result = @[]
for item in lst:
when compiles(item is T):
if item is T:
result.add(item)
# Usage
echo isType("Hello", string)
echo isType(42, int)
var mixed: seq[RootObj] = @[1, 2, "Hello", 3.14, "World"] # not allowed; use object variants
# Inline function using lambda (single expression)
let square = proc(x: int): int = x * x- Line comment:
# comment - Block comment:
#[ block comment ]# - Documentation:
## doc comment(for nim doc)
# Higher-Order Functions in Nim
# Function that takes a function as parameter
proc applyOperation(a, b: int, operation: proc(x, y: int): int): int =
return operation(a, b)
# Function that returns a function
proc getMultiplier(factor: int): proc(x: int): int =
return proc(x: int): int = x * factor
# Function composition
proc compose[A,B,C](f: proc(x: B): C, g: proc(x: A): B): proc(x: A): C =
return proc(x: A): C = f(g(x))
# Higher-order function with multiple lambdas
proc process(value: int, transform: proc(x: int): int, filterFunc: proc(x: int): bool): Option[int] =
if filterFunc(value):
return some(transform(value))
else:
return none(int)
# Usage with lambda
echo applyOperation(10, 20, proc(a, b: int): int = a + b)
let double = getMultiplier(2)
echo double(5)
let square = proc(x: int): int = x * x
let addTen = proc(x: int): int = x + 10
let squareThenAddTen = compose(addTen, square)
echo squareThenAddTen(5)
# Using named function
proc add(a, b: int): int = a + b
echo applyOperation(10, 20, add)
# Built-in higher-order functions (using sequtils)
import sequtils
var numbers = @[1, 2, 3, 4, 5]
let squared = numbers.map(proc(x: int): int = x * x)
let even = numbers.filter(proc(x: int): bool = x mod 2 == 0)
let sumAll = numbers.foldl(a + b)
echo squared
echo even
echo sumAllGenerics allow writing code that works with multiple types while preserving type safety. They are defined using type parameters.
- Generic proc:
proc add[T](a, b: T): T = a + b - Generic type:
type Box[T] = object value: T - Constraints:
proc f[T: SomeNumber](x: T)
# Async/Await in Nim
import std/asyncdispatch
import std/times
# Basic coroutine
proc fetchData(): Future[string] {.async.} =
await sleepAsync(1000) # Simulate network call
return "Data loaded"
# Run coroutine
proc mainLaunch() {.async.} =
let task = fetchData()
let result = await task
echo result
# Multiple coroutines
proc parallelTasks() {.async.} =
let task1 = fetchData()
let task2 = fetchData()
let results = await all([task1, task2])
echo "Results: ", results
# Timeout
proc withTimeout() {.async.} =
try:
let result = await withTimeout(fetchData(), 500)
echo result
except TimeoutError:
echo "Timed out!"
# Structured concurrency (using asyncCheck)
proc structuredConcurrency() {.async.} =
var tasks: seq[Future[void]] = @[]
tasks.add(sleepAsync(1000))
tasks.add(sleepAsync(500))
await all(tasks)
echo "All tasks completed"
# Usage
proc main() {.async.} =
await mainLaunch()
await parallelTasks()
await withTimeout()
await structuredConcurrency()
waitFor main()Templates are a simple form of metaprogramming that substitute code at compile time, similar to C macros but with hygiene.
- Syntax:
template log(msg: string) = echo "[LOG] ", msg - Unhygienic: can access caller’s scope
- Use: for code reuse and compile‑time evaluation
# Generators (Iterators) in Nim
# Nim uses iterators instead of generators
# Simple iterator
iterator simpleGenerator(n: int): int =
for i in 1..n:
yield i
# Iterator with operations
iterator evenNumbers(n: int): int =
for i in 0..<n:
if i mod 2 == 0:
yield i
# Iterator with map (using transform)
iterator doubleGenerator(iter: iterator(): int): int =
for value in iter():
yield value * 2
# Filter iterator
iterator filterGenerator(iter: iterator(): int, pred: proc(x: int): bool): int =
for value in iter():
if pred(value):
yield value
# Usage
for i in simpleGenerator(5):
echo i
for i in evenNumbers(10):
echo i
# Collect from iterator into sequence
import sequtils
let evens = toSeq(evenNumbers(10))
echo evens
# Async iterator (not directly, but use async procs with yield)Macros are powerful metaprogramming tools that operate on Nim’s abstract syntax tree (AST) at compile time, enabling code generation and DSLs.
- Definition:
macro myMacro(arg: untyped): untyped = ... - AST manipulation: using
newLit,newCall, etc. - Use: custom syntax, compile‑time checks
# Queues in Nim
import std/queues
import std/asyncdispatch
import std/channels
# Simple queue using seq
type SimpleQueue[T] = object
items: seq[T]
proc newSimpleQueue[T](): SimpleQueue[T] =
result.items = @[]
proc put[T](q: var SimpleQueue[T], item: T) =
q.items.add(item)
proc get[T](q: var SimpleQueue[T]): Option[T] =
if q.items.len > 0:
return some(q.items[0])
else:
return none(T)
# Async queue (using channels)
proc asyncQueueExample() {.async.} =
var q: Channel[int]
q.open()
# Producer
proc producer() {.async.} =
for i in 0..4:
await q.send(i)
echo "Produced: ", i
await sleepAsync(100)
await q.send(-1) # Sentinel
# Consumer
proc consumer() {.async.} =
while true:
let item = await q.recv()
if item == -1:
break
echo "Consumed: ", item
await sleepAsync(150)
await all([producer(), consumer()])
# Thread-safe queue using std/queues
import std/threadpool
proc threadQueueExample() =
var q: Queue[int]
q.init()
proc producer() =
for i in 0..4:
q.push(i)
echo "Produced: ", i
proc consumer() =
while true:
let item = q.pop()
if item == -1:
break
echo "Consumed: ", item
spawn producer()
spawn consumer()
sync()
# Usage
waitFor asyncQueueExample()Nim uses try, except, finally for exception handling. Exceptions are objects inheriting from Exception.
- Try:
try: ... except: ... finally: ... - Custom:
type MyError = object of Exception - Raising:
raise newException(MyError, "msg")
# Enums and Sealed Types in Nim
# Enum class
type
Color = enum
red = 1, green = 2, blue = 3
Status = enum
success = 200
error = 500
loading = 100
# Sealed types using object variants
type
UiStateKind = enum
successKind, errorKind, loadingKind, idleKind
UiState = object
case kind: UiStateKind
of successKind:
data: string
of errorKind:
message: string
of loadingKind, idleKind:
discard
# Another sealed type for payment
type
PaymentKind = enum
cashKind, creditCardKind, payPalKind
Payment = object
case kind: PaymentKind
of cashKind:
amount: float
of creditCardKind:
number: string
expiry: string
of payPalKind:
email: string
# Handling functions
proc handleState(state: UiState): string =
case state.kind
of successKind:
return "Data: " & state.data
of errorKind:
return "Error: " & state.message
of loadingKind:
return "Loading..."
of idleKind:
return "Idle"
proc handlePayment(payment: Payment): string =
case payment.kind
of cashKind:
return "Cash amount: " & $payment.amount
of creditCardKind:
return "Card: " & payment.number
of payPalKind:
return "PayPal: " & payment.email
# Usage
echo handleState(UiState(kind: successKind, data: "Data loaded"))
echo handlePayment(Payment(kind: cashKind, amount: 100.0))nil represents a null reference for pointer and ref types. Dereferencing nil is undefined behavior.
- Ref types:
var p: ref int = nil - Check:
if p != nil: echo p[] - Option types: prefer
Optionfor safety
# Generics in Nim
# Generic type
type
Box[T] = object
value: T
proc newBox[T](val: T): Box[T] =
Box[T](value: val)
proc getValue[T](box: Box[T]): T =
return box.value
# Generic procedure
proc swap[T](a, b: var T) =
let tmp = a
a = b
b = tmp
# Generic with constraints (using type classes)
proc sumItems[T: SomeNumber](items: seq[T]): T =
var result: T = 0
for item in items:
result += item
return result
# Variance (Nim uses invariant generics by default)
# Covariant not directly, but we can use inheritance with refs
type
Producer[T] = object
data: T
proc produce[T](p: Producer[T]): T = p.data
# Contravariant (not directly, but we can use method overloading)
# Usage
var box = newBox("Hello")
echo getValue(box)
var a = 1; var b = 2
swap(a, b)
echo a, " ", b
echo sumItems(@[1, 2, 3, 4, 5])Nim provides Option[T] (in std/options) for optional values and Result[T, E] for fallible operations.
- Option:
some(value)ornone(T) - Result:
ok(value)orerr(error) - Use: safe handling of nulls and errors
# Delegation in Nim (using composition)
type
Repository = object
# Base methods
proc getData(r: Repository): string = "Data from repo"
proc saveData(r: var Repository, data: string) = discard
type
DatabaseRepository = object
# no extra fields
proc getData(db: DatabaseRepository): string = "Data from database"
proc saveData(db: var DatabaseRepository, data: string) =
echo "Saving to database: ", data
type
CachedRepository = object
repo: DatabaseRepository
cache: Option[string]
proc getData(cr: var CachedRepository): string =
if cr.cache.isNone:
cr.cache = some(cr.repo.getData())
return cr.cache.get()
proc saveData(cr: var CachedRepository, data: string) =
cr.repo.saveData(data)
cr.cache = none(string) # Invalidate cache
# Lazy property using a closure
type
LazyValue[T] = object
compute: proc(): T
value: Option[T]
proc get[T](lv: var LazyValue[T]): T =
if lv.value.isNone:
lv.value = some(lv.compute())
return lv.value.get()
# Observable property (using setter/getter)
type
ObservableProperty[T] = object
value: T
observers: seq[proc(oldVal, newVal: T)]
proc setVal[T](op: var ObservableProperty[T], newVal: T) =
let old = op.value
op.value = newVal
for obs in op.observers:
obs(old, newVal)
# Usage
var db = DatabaseRepository()
var cached = CachedRepository(repo: db)
echo cached.getData()
cached.saveData("new data")
echo cached.getData() # will fetch from db again- ref: garbage‑collected or traced pointer; safe, managed
- ptr: unmanaged pointer; unsafe, for low‑level code
- Memory:
refpoints to objects on heap (GC),ptrcan point anywhere
# Singleton Pattern in Nim
# Using module-level variables (singleton by nature)
# In a module app_config.nim:
let API_URL* = "https://api.example.com"
let TIMEOUT* = 5000
proc printConfig*() =
echo "API URL: ", API_URL
echo "Timeout: ", TIMEOUT
# Using a global variable with getter
type AppConfig = object
apiUrl: string
timeout: int
var instance: AppConfig
proc getAppConfig(): AppConfig =
if instance.apiUrl == "":
instance = AppConfig(apiUrl: "https://api.example.com", timeout: 5000)
return instance
# Using a type with a static field (not directly, but can use a ref and a proc)
type AppConfigRef = ref object
apiUrl: string
timeout: int
var appConfigInstance: AppConfigRef
proc getAppConfigRef(): AppConfigRef =
if appConfigInstance.isNil:
appConfigInstance = AppConfigRef(apiUrl: "https://api.example.com", timeout: 5000)
return appConfigInstance
# Using a constructor with a global variable
var config: AppConfig
proc initConfig() =
if config.apiUrl == "":
config = AppConfig(apiUrl: "https://api.example.com", timeout: 5000)
# Usage
initConfig()
echo config.apiUrlNim offers several memory management strategies: GC (refc, arc, orc) and manual via ptr and alloc.
- --gc:refc: reference counting (old)
- --gc:arc: automatic reference counting (fast, deterministic)
- --gc:orc: ORC (ARC with cycle detection)
- Manual:
alloc,deallocfromsystem
# DSL (Domain Specific Language) in Nim
# Using templates and macros
# HTML DSL
import strutils
template html(body: untyped): string =
"<html>" & body & "</html>"
template body(body: untyped): string =
"<body>" & body & "</body>"
template h1(text: string): string =
"<h1>" & text & "</h1>"
template p(text: string): string =
"<p>" & text & "</p>"
# Usage
let page = html:
body:
h1("Welcome") & p("Paragraph")
echo page
# Builder DSL
type UserBuilder = object
name: string
age: int
email: string
proc name(b: var UserBuilder, n: string): var UserBuilder = b.name = n; b
proc age(b: var UserBuilder, a: int): var UserBuilder = b.age = a; b
proc email(b: var UserBuilder, e: string): var UserBuilder = b.email = e; b
proc build(b: UserBuilder): tuple[name: string, age: int, email: string] =
(name: b.name, age: b.age, email: b.email)
# Usage
var builder = UserBuilder()
let user = builder.name("Alice").age(25).email("alice@example.com").build()
echo user
# Query DSL (using a template)
type Query = object
table: string
whereClause: string
orderClause: string
limitClause: string
proc where(q: var Query, cond: string): var Query = q.whereClause = cond; q
proc orderBy(q: var Query, field: string, direction = "ASC"): var Query =
q.orderClause = "ORDER BY " & field & " " & direction; q
proc limit(q: var Query, count: int): var Query =
q.limitClause = "LIMIT " & $count; q
proc execute(q: Query): string =
result = "SELECT * FROM " & q.table
if q.whereClause != "": result.add(" WHERE " & q.whereClause)
if q.orderClause != "": result.add(" " & q.orderClause)
if q.limitClause != "": result.add(" " & q.limitClause)
# Usage
var q = Query(table: "users")
let sql = q.where("age > 18").orderBy("name").limit(10).execute()
echo sqlNim’s garbage collector (GC) manages memory automatically. The default is ORC (since Nim 1.6), which is a cycle‑collecting reference counting system.
- ORC: efficient, low latency, no stop‑the‑world
- ARC: simple reference counting
- Refc: older, with cycle detection via GC
# Decorators (using templates and macros)
# Nim doesn't have decorators like Python, but we can use templates or macros.
# Basic decorator (using template)
template myDecorator(body: untyped): untyped =
echo "Before function"
body
echo "After function"
# Usage
myDecorator:
echo "Hello"
# Decorator with parameters (using macro)
import macros
macro repeatDecorator(n: int, body: untyped): untyped =
result = newStmtList()
for i in 0..<n:
result.add(body)
# Usage
repeatDecorator(3):
echo "Hello!"
# Class decorator (using a macro)
macro addMethod(cls: typedesc): untyped =
let newMethod = quote do:
proc newMethod(self: `cls`): string =
return "New method added"
result = newStmtList(newMethod)
# Usage
type MyClass = object
addMethod(MyClass)
var obj = MyClass()
echo obj.newMethod()
# Property decorator (using getter/setter)
type Person = object
name: string
proc name(p: Person): string = p.name
proc `name=`(p: var Person, value: string) = p.name = value
# Usage
var p = Person(name: "Alice")
echo p.name
p.name = "Bob"
echo p.namenew allocates memory for reference types (ref). It returns a reference to a newly created object.
- Allocation:
var p = new(Person) - Initialization:
p.name = "John" - Alternative:
Person(name: "John")(also creates ref)
# Reflection in Nim
import std/macros
import std/typetraits
# Basic reflection using typetraits
type Person = object
name: string
age: int
city: string
proc greet(p: Person): string = "Hello, my name is " & p.name
proc updateAge(p: var Person, newAge: int) = p.age = newAge
# List fields
proc listFields(T: typedesc): seq[string] =
result = @[]
for field in fields(T):
result.add(field.name)
# Access properties using getField
proc getField[T](obj: T, name: string): string =
for field in fields(obj):
if field.name == name:
return $field.value
return ""
# Call functions dynamically (using Nim's runtime)
proc callProc(procName: string, obj: var Person, args: varargs[string]) =
# Not straightforward in Nim without macros
discard
# Create instance dynamically (using default constructor)
proc createInstance(T: typedesc): T =
return T.default
# Introspection
proc introspect(obj: any) =
echo "Type: ", obj.type.name
echo "Fields:"
for field in fields(obj):
echo field.name, " = ", field.value
# Usage
var person = Person(name: "Alice", age: 25, city: "NYC")
echo listFields(Person)
echo getField(person, "name")
let newPerson = createInstance(Person)
introspect(person)Iterators are like procedures but can yield multiple values using yield. They are used in for loops.
- Definition:
iterator myIter(a: int): int = yield a*2 - Yield:
yield value - State: iterators maintain state between yields
# Context Managers (using with template)
import std/with
# File context manager (using with)
var f: File
with f = open("test.txt", fmWrite):
f.write("Hello World")
# Custom context manager using a template
template withFile(filename: string, mode: FileMode, body: untyped): untyped =
var f: File
if open(f, filename, mode):
try:
body
finally:
close(f)
# Usage
withFile("test.txt", fmRead):
echo f.readAll()
# Timer context manager (using a template)
template timer(body: untyped): untyped =
let start = cpuTime()
body
let elapsed = cpuTime() - start
echo "Time: ", elapsed, "s"
# Usage
timer:
sleep(100)
echo "Operation completed"Closures are procedures that capture variables from their enclosing scope. Nim supports closures via proc with environment.
- Capture:
let f = proc(x: int): int = x + y(where y is outer) - Closure type:
proc (int): int - Memory: closure environment is stored on heap
# Threading and Concurrency in Nim
import std/threadpool
import std/locks
import std/times
# Basic thread (using spawn)
proc fetchData(): string =
sleep(1000)
return "Data loaded"
# Thread with lock
var counter: int
var lock: Lock
initLock(lock)
proc increment() =
withLock(lock):
counter += 1
# Thread pool
proc processTask(taskId: int): string =
echo "Processing task ", taskId
sleep(500)
return "Task " & $taskId & " completed"
# Thread-local data (using threadvar)
var threadLocal: int # each thread gets its own copy
proc worker() =
threadLocal = 10
echo "ThreadLocal: ", threadLocal
# Usage
proc counterExample() =
var threads: seq[Thread[void]]
for i in 0..<1000:
threads.createThread(proc() = increment())
for t in threads:
joinThread(t)
echo "Final count: ", counter
# Thread pool example
proc threadPoolExample() =
var results: seq[FlowVar[string]]
for i in 0..<10:
results.add(spawn processTask(i))
for result in results:
echo ^result
# Run examples
# worker() # not needed, just example
counterExample()
threadPoolExample()Nim supports asynchronous programming with async and await (via std/asyncdispatch).
- Async proc:
proc asyncProc(): Future[string] = ... - Await:
let result = await asyncProc() - Event loop:
waitFor(asyncProc())
# Generators and Iterators (already covered in Q22)
# Just an additional example with custom iterator
iterator fibonacci(n: int): int =
var a = 0; var b = 1
for i in 0..<n:
yield a
(a, b) = (b, a + b)
iterator evenNumbers(n: int): int =
for i in 0..<n:
if i mod 2 == 0:
yield i
# Custom iterator with send (using closure iterator)
iterator accumulator(): int =
var total = 0
while true:
let value = yield total
if value == -1: break
total += value
# Usage
for i in fibonacci(10):
echo i
for i in evenNumbers(10):
echo i
# Using closure iterator
var acc = accumulator()
echo acc() # start
echo acc(10) # total = 10
echo acc(20) # total = 30Calling conventions define how parameters are passed and stack is managed. Nim supports stdcall, cdecl, fastcall, thiscall, etc.
- Pragmas:
{.cdecl.},{.stdcall.} - Default:
nimcall(optimized for Nim) - Interop: used for calling C/JavaScript functions
# Asyncio and Event Loops (already covered, but more examples)
import std/asyncdispatch
import std/asyncfutures
# Basic async function
proc asyncFetch(): Future[string] {.async.} =
await sleepAsync(1000)
return "Data loaded"
# Multiple async tasks
proc parallelAsync() {.async.} =
let tasks = @[asyncFetch(), asyncFetch()]
let results = await all(tasks)
echo "Results: ", results
# Async with timeout
proc asyncTimeout() {.async.} =
try:
let result = await withTimeout(asyncFetch(), 500)
echo result
except TimeoutError:
echo "Timed out!"
# Async generator (using async iterator)
iterator asyncNumbers(n: int): Future[int] {.async.} =
for i in 0..<n:
await sleepAsync(100)
yield i
# Async context manager (using with)
type AsyncResource = object
proc openAsync(): Future[AsyncResource] {.async.} =
await sleepAsync(100)
return AsyncResource()
proc closeAsync(r: AsyncResource) {.async.} =
await sleepAsync(100)
template withAsyncResource(body: untyped): untyped =
let resource = waitFor openAsync()
try:
body
finally:
waitFor closeAsync(resource)
# Usage
proc main() {.async.} =
await parallelAsync()
await asyncTimeout()
for num in asyncNumbers(5):
echo num
waitFor main()Nim provides FFI (Foreign Function Interface) via importc, importcpp, and importjs pragmas to call external C, C++, or JavaScript code.
- C:
proc rand(): int {.importc, header: "<stdlib.h>".} - C++:
proc myCppFunc() {.importcpp.} - JavaScript:
proc alert(msg: cstring) {.importjs.}
# Descriptors in Nim (using getter/setter)
# Nim doesn't have descriptors, but we can use properties with getter/setter.
type
Person = object
age: int
proc age(p: Person): int = p.age
proc `age=`(p: var Person, value: int) =
if value < 0:
raise newException(ValueError, "Age must be positive")
p.age = value
# Property with validation
type
PositiveNumber = object
value: int
proc setPositive(n: var PositiveNumber, val: int) =
if val < 0:
raise newException(ValueError, "Must be positive")
n.value = val
proc getPositive(n: PositiveNumber): int = n.value
# Usage
var p = Person(age: 25)
echo p.age
p.age = 30
try:
p.age = -5
except ValueError:
echo "Invalid age"Pragmas are compiler directives or annotations that control code generation, warnings, and linking.
- Examples:
{.inline.},{.noSideEffect.},{.deprecated.} - Link:
{.passL: "-lssl".} - Export:
{.exportc.}
# Exception Handling in Async Code
import std/asyncdispatch
# Try-catch in async proc
proc asyncDivide(a, b: int): Future[int] {.async.} =
try:
return a div b
except DivByZeroError:
return 0
# Exception handler for tasks
proc exceptionHandler() {.async.} =
proc taskWithError(): Future[void] {.async.} =
await sleepAsync(100)
raise newException(ValueError, "Task error")
try:
let results = await all(@[asyncDivide(10, 2), asyncDivide(10, 0), taskWithError()])
for r in results:
echo r
except:
echo "Error in gather: ", getCurrentExceptionMsg()
# Custom exception in async
type AsyncTimeoutError = object of Exception
proc asyncTimeoutRaise() {.async.} =
try:
await sleepAsync(2000)
except CancelledError:
raise newException(AsyncTimeoutError, "Operation timed out")
# Async context manager with exception
type AsyncResource = object
proc openAsync(): Future[AsyncResource] {.async.} = return AsyncResource()
proc closeAsync(r: AsyncResource) {.async.} = discard
template withAsyncResource(body: untyped): untyped =
let r = waitFor openAsync()
try:
body
finally:
waitFor closeAsync(r)
# Usage
proc main() {.async.} =
await exceptionHandler()
try:
let task = asyncTimeoutRaise()
await withTimeout(task, 500)
except AsyncTimeoutError:
echo "Custom timeout"
except TimeoutError:
echo "Standard timeout"
waitFor main()Distinct types create new types that are not compatible with their base type, providing strong type safety.
- Declaration:
type UserId = distinct int - Conversion:
UserId(10) - No implicit: must be explicit
# Producer-Consumer Pattern
import std/asyncdispatch
import std/channels
# Async producer-consumer
proc asyncProducerConsumer() {.async.} =
var q: Channel[int]
q.open(10)
proc producer() {.async.} =
for i in 0..19:
await q.send(i)
echo "Produced: ", i
await sleepAsync(100)
await q.send(-1) # Sentinel
proc consumer() {.async.} =
while true:
let item = await q.recv()
if item == -1:
break
echo "Consumed: ", item
await sleepAsync(150)
await all([producer(), consumer()])
# Fan-out pattern
proc fanOut() {.async.} =
var q: Channel[int]
q.open()
proc producer() {.async.} =
for i in 0..19:
await q.send(i)
await q.send(-1)
proc consumer(id: int) {.async.} =
while true:
let item = await q.recv()
if item == -1:
await q.send(-1) # Pass sentinel
break
echo "Consumer ", id, ": ", item
await sleepAsync(100)
await all([producer(), consumer(1), consumer(2), consumer(3)])
# Fan-in pattern
proc fanIn() {.async.} =
var q: Channel[string]
q.open()
proc producer(id: int) {.async.} =
for i in 0..4:
await q.send("Producer " & $id & ": " & $i)
await sleepAsync(50)
proc consumer() {.async.} =
var received = 0
while received < 15:
let item = await q.recv()
echo item
received += 1
var prods: seq[Future[void]]
for i in 0..2:
prods.add(producer(i))
await all(prods)
await consumer()
# Usage
waitFor asyncProducerConsumer()
waitFor fanOut()
waitFor fanIn()Type aliases give alternative names to existing types. Use type with an equals sign.
- Syntax:
type MyInt = int - Compatibility: alias is equivalent to original
- Use: improve readability
# Cancellation in Nim
import std/asyncdispatch
# Cooperative cancellation
proc cooperativeCancellation() {.async.} =
var cancelled = false
proc worker() {.async.} =
var i = 0
while not cancelled and i < 100:
echo "Working: ", i
inc i
await sleepAsync(50)
let task = worker()
await sleepAsync(200)
cancelled = true
await task
# Cancellation with finally
proc cancellationFinally() {.async.} =
proc worker() {.async.} =
try:
for i in 0..99:
echo "Processing: ", i
await sleepAsync(100)
finally:
echo "Cleaning up"
await sleepAsync(100)
echo "Cleanup done"
let task = worker()
await sleepAsync(250)
task.cancel()
try:
await task
except CancelledError:
echo "Task cancelled"
# Cancellation with timeout
proc cancellationTimeout() {.async.} =
proc asyncWorker() {.async.} =
for i in 0..9:
await sleepAsync(200)
echo "Iteration: ", i
try:
await withTimeout(asyncWorker(), 1000)
except TimeoutError:
echo "Timed out"
# Custom cancellation check
proc customCancellation() {.async.} =
var cancelled = false
proc worker() {.async.} =
var i = 0
while i < 1000:
if i mod 100 == 0:
echo "Still running: ", i
inc i
await sleepAsync(1)
let task = worker()
await sleepAsync(100)
task.cancel()
try:
await task
except CancelledError:
echo "Cancelled"
# Usage
waitFor cooperativeCancellation()
waitFor cancellationFinally()
waitFor cancellationTimeout()
waitFor customCancellation()Enums define a type with a fixed set of values, each with an integer value.
- Declaration:
type Color = enum red, green, blue - Ordinal:
ord(red) = 0 - Custom values:
enum red = 1, green = 2, blue = 4
# Testing in Nim
import unittest
import std/asyncdispatch
# Using unittest module
suite "Test Calculator":
var calc: Calculator
setup:
calc = newCalculator()
test "add":
check calc.add(2, 3) == 5
check calc.add(-1, 1) == 0
test "divide":
check calc.divide(10, 2) == 5
expect DivByZeroError:
discard calc.divide(10, 0)
# Async test
proc asyncFetch(): Future[string] {.async.} =
await sleepAsync(100)
return "Data loaded"
test "async function":
check waitFor(asyncFetch()) == "Data loaded"
# Example Calculator type
type Calculator = object
proc newCalculator(): Calculator = Calculator()
proc add(c: Calculator, a, b: int): int = a + b
proc divide(c: Calculator, a, b: int): int = a div b
# Run tests (when compiled with -r)
when isMainModule:
runTests()discard explicitly ignores the return value of an expression, silencing the "unused return" warning.
- Use:
discard myProc() - Optional: often used with side‑effect‑only procs
# Nim Multiplatform
import std/os
import std/strutils
# Platform detection
proc platformName(): string =
when defined(windows):
return "Windows"
elif defined(macOS):
return "macOS"
elif defined(linux):
return "Linux"
else:
return "Unknown"
proc greet(): string =
return "Hello from " & platformName()
# Platform-specific code using when
proc getPlatformInfo(): string =
return "System: " & hostOS & ", CPU: " & hostCPU
# Platform-specific class (using an object)
type Platform = object
name: string
info: string
proc newPlatform(): Platform =
return Platform(name: platformName(), info: getPlatformInfo())
proc getVersion(p: Platform): string = p.info
# Serialization using JSON
import std/json
type User = object
id: int
name: string
email: string
proc encodeUser(u: User): string =
return %*{"id": u.id, "name": u.name, "email": u.email}.pretty()
proc decodeUser(data: string): User =
let json = parseJson(data)
return User(id: json["id"].getInt(), name: json["name"].getStr(), email: json["email"].getStr())
# Usage
echo greet()
var p = newPlatform()
echo p.info
var user = User(id: 1, name: "Alice", email: "alice@example.com")
let encoded = encodeUser(user)
let decoded = decodeUser(encoded)
echo decoded.nameNim strings are mutable, 0‑based, and support concatenation, slicing, and many utilities.
- Concat:
&oradd - Slicing:
s[0..3] - Length:
len(s) - Search:
find(s, "sub")
# Reverse a string
proc reverseString(s: string): string =
result = ""
for i in countdown(s.len-1, 0):
result.add(s[i])
# Using built-in (std/algorithm)
import std/algorithm
proc reverseStringAlg(s: string): string =
var tmp = s
reverse(tmp) # in-place
return tmp
echo reverseString("hello") # "olleh"
echo reverseStringAlg("hello")Nim provides system file I/O procs and the std/streams module for flexible reading/writing.
- Read:
let contents = readFile("file.txt") - Write:
writeFile("out.txt", "data") - Streams:
var f = open("file", fmRead)
# Check palindrome
proc isPalindrome(s: string): bool =
let cleaned = s.filterIt(it.isAlphaNumeric).toLower()
return cleaned == cleaned.reversed()
echo isPalindrome("racecar") # true
echo isPalindrome("hello") # false
# Two-pointer approach
proc isPalindromeTwoPointer(s: string): bool =
var cleaned = s.filterIt(it.isAlphaNumeric).toLower()
var left = 0
var right = cleaned.len - 1
while left < right:
if cleaned[left] != cleaned[right]:
return false
inc left
dec right
return trueNim provides regex support via std/re for pattern matching.
- Match:
if match("abc", re"\w+"): - Replace:
replace(s, re"a", "b") - Groups:
let m = match(s, re"(\d+)")
# Find max in array
proc findMax[T: SomeNumber](arr: seq[T]): T =
assert(arr.len > 0, "Empty array")
var maxVal = arr[0]
for i in 1..<arr.len:
if arr[i] > maxVal:
maxVal = arr[i]
return maxVal
echo findMax(@[1, 5, 3, 9, 2]) # 9
# Using built-in
import std/algorithm
echo max(@[1, 5, 3, 9, 2])The system module is implicitly imported and provides core functionality like echo, len, add, memory management, etc.
- Built‑in:
echo,quit - Memory:
alloc,dealloc - Type helpers:
typeof,addr
# Remove duplicates
proc removeDuplicates[T](arr: seq[T]): seq[T] =
var seen: HashSet[T]
result = @[]
for item in arr:
if item notin seen:
seen.incl(item)
result.add(item)
echo removeDuplicates(@[1, 2, 2, 3, 3, 4]) # [1, 2, 3, 4]
# Using a set (preserves order not guaranteed)
proc removeDuplicatesSet[T](arr: seq[T]): seq[T] =
result = toSeq(union(arr))
# Using list comprehension (not directly, but we can filter)
proc removeDuplicatesComprehension[T](arr: seq[T]): seq[T] =
var seen: HashSet[T]
arr.filterIt(if it notin seen: (seen.incl(it); true) else: false)math provides mathematical functions: trigonometric, exponential, logarithmic, etc.
- Import:
import math - Functions:
sqrt,sin,cos,pow,ln - Constants:
PI,E
# Merge arrays
proc mergeArrays[T](a, b: seq[T]): seq[T] =
result = a & b
echo mergeArrays(@[1, 2], @[3, 4]) # [1, 2, 3, 4]
# Using concat (already works)
proc mergeArraysExtend[T](a, b: seq[T]): seq[T] =
result = a
result.add(b)
# Merge and remove duplicates
proc mergeUnique[T](a, b: seq[T]): seq[T] =
let combined = a & b
return removeDuplicates(combined)times provides date, time, and duration handling.
- Now:
let now = now() - Format:
now.format("yyyy-MM-dd") - Duration:
var d = initDuration(minutes=5)
# Convert string to number
proc stringToNumber(s: string): int =
try:
return parseInt(s)
except ValueError:
return 0
echo stringToNumber("42") # 42
# Safe conversion returning Option
import std/options
proc stringToNumberSafe(s: string): Option[int] =
try:
return some(parseInt(s))
except ValueError:
return none(int)
# Convert to float
proc stringToFloat(s: string): Option[float] =
try:
return some(parseFloat(s))
except ValueError:
return none(float)random provides random number generation.
- Seed:
randomize() - Rand:
rand(100)(0..100) - Float:
rand(1.0)
# Loop through dictionary (table)
import tables
proc loopDict(d: Table[string, string]) =
for key, value in d:
echo key, " => ", value
# Using pairs
proc loopDictPairs(d: Table[string, string]) =
for (key, value) in d:
echo key, " => ", value
# Loop through keys
proc loopDictKeys(d: Table[string, string]) =
for key in keys(d):
echo key, " => ", d[key]
var data = {"name": "Alice", "age": "25", "city": "NYC"}.toTable
loopDict(data)Nimble is Nim’s package manager. Create a .nimble file describing dependencies, version, and tasks.
- Initialize:
nimble init - Dependencies:
requires "nim >= 1.6.0" - Tasks: define custom build/test tasks
# Delay function execution
import std/asyncdispatch
import std/times
# Using async (non-blocking)
proc delayedExecution(delayMs: int, action: proc()) {.async.} =
await sleepAsync(delayMs)
action()
# Using sleep (blocking)
proc delayedExecutionBlocking(delayMs: int, action: proc()) =
sleep(delayMs)
action()
# Usage
proc myAction() = echo "After 2 seconds"
waitFor delayedExecution(2000, myAction)Nim can execute code at compile time using static blocks, const, and compile‑time function evaluation (CTFE).
- Const: evaluated at compile time
- Static:
static: echo "compile time" - Macros: execute during compilation
# HTTP GET request
import std/httpclient
import std/json
# Synchronous GET
proc fetchData(url: string): JsonNode =
let client = newHttpClient()
try:
let response = client.get(url)
if response.code == 200:
return parseJson(response.body)
else:
return nil
except:
echo "Error: ", getCurrentExceptionMsg()
return nil
# Asynchronous GET
import std/asyncdispatch
proc fetchDataAsync(url: string): Future[JsonNode] {.async.} =
let client = newAsyncHttpClient()
try:
let response = await client.get(url)
if response.code == 200:
return parseJson(response.body)
else:
return nil
except:
echo "Error: ", getCurrentExceptionMsg()
return nil
# GET with headers
proc fetchWithHeaders(url: string, headers: HttpHeaders): JsonNode =
let client = newHttpClient()
client.headers = headers
try:
let response = client.get(url)
return parseJson(response.body)
except:
return nil
# Usage
# let data = fetchData("https://api.example.com/data")Concepts (experimental) define a set of requirements that a type must satisfy, used to constrain generic parameters.
- Definition:
type MyConcept = concept x - Usage:
proc f[T: MyConcept](x: T) - Multiple: combine with
and
# Create a promise-like Future
import std/asyncdispatch
proc createFuture(shouldResolve: bool): Future[string] {.async.} =
await sleepAsync(1000)
if shouldResolve:
return "Success!"
else:
raise newException(Exception, "Failed!")
# Usage
proc main() {.async.} =
try:
let result = await createFuture(true)
echo result
except:
echo "Caught: ", getCurrentExceptionMsg()
waitFor main()Metaprogramming in Nim includes macros, templates, compile‑time evaluation, and pragmas to generate or transform code.
- Macros: AST manipulation
- Templates: simple substitution
- Pragmas: compiler directives
- Static: compile‑time code execution
# Factorial
proc factorial(n: int): int =
if n <= 1: 1
else: n * factorial(n-1)
echo factorial(5) # 120
# Iterative
proc factorialIterative(n: int): int =
result = 1
for i in 2..n:
result *= i
# Using math (not built-in, but can implement)
# no built-in factorial, but we can use our ownDSLs in Nim are often built using templates, macros, and the with pattern. For example, a SQL or HTML builder.
- Template:
template html(body) = "<html>" & body & "</html>" - Macro: parse custom syntax at compile time
- Fluent interface: method chaining
# Fibonacci
proc fibonacci(n: int): int =
if n <= 1: n
else: fibonacci(n-1) + fibonacci(n-2)
echo fibonacci(8) # 21
# Iterative
proc fibonacciIterative(n: int): int =
if n <= 1: return n
var a = 0; var b = 1
for i in 2..n:
(a, b) = (b, a + b)
return b
# Memoized version (using a table)
import tables
proc fibonacciMemo(n: int, memo: var Table[int, int]): int =
if n in memo: return memo[n]
if n <= 1: return n
result = fibonacciMemo(n-1, memo) + fibonacciMemo(n-2, memo)
memo[n] = result
var memo = initTable[int, int]()
echo fibonacciMemo(10, memo)Nim’s effect system tracks side effects (e.g., I/O, exceptions) and can enforce purity with the {.noSideEffect.} pragma.
- Effects:
io,time,write,exception - Pragma:
{.noSideEffect.}ensures pure - Tag:
{.effects.}for custom effects
# FizzBuzz
proc fizzbuzz(n: int) =
for i in 1..n:
if i mod 15 == 0:
echo "FizzBuzz"
elif i mod 3 == 0:
echo "Fizz"
elif i mod 5 == 0:
echo "Buzz"
else:
echo i
fizzbuzz(15)
# Return seq
proc fizzbuzzList(n: int): seq[string] =
result = @[]
for i in 1..n:
if i mod 15 == 0:
result.add("FizzBuzz")
elif i mod 3 == 0:
result.add("Fizz")
elif i mod 5 == 0:
result.add("Buzz")
else:
result.add($i)Use threadvar to declare thread‑local variables. Each thread gets its own copy.
- Declaration:
threadvar counter: int - Access: each thread sees its own
counter - Safe: no locking needed for per‑thread data
# Find missing number
proc findMissing(arr: seq[int]): int =
let n = arr.len + 1
let total = n * (n + 1) div 2
let sum = arr.foldl(a + b, 0)
return total - sum
echo findMissing(@[1, 2, 4, 5, 6]) # 3
# Using XOR
proc findMissingXor(arr: seq[int]): int =
let n = arr.len + 1
var xorSum = 0
for i in 1..n:
xorSum = xorSum xor i
for num in arr:
xorSum = xorSum xor num
return xorSumChannels (from std/channels) enable safe communication between threads.
- Create:
var chan: Channel[int] - Send:
chan.send(42) - Receive:
let x = chan.recv()
# Find duplicates
proc findDuplicates[T](arr: seq[T]): seq[T] =
var seen: HashSet[T]
var duplicates: HashSet[T]
for item in arr:
if item in seen:
duplicates.incl(item)
else:
seen.incl(item)
return toSeq(duplicates)
echo findDuplicates(@[1, 2, 3, 2, 4, 3]) # [2, 3]
# Using a table to count
import tables
proc findDuplicatesCount[T](arr: seq[T]): seq[T] =
var counter = initCountTable[T]()
for item in arr:
counter.inc(item)
result = @[]
for k, v in counter:
if v > 1:
result.add(k)Nim’s asynchronous I/O uses a reactor pattern via std/asyncdispatch to handle multiple events in a single thread.
- Event loop:
runForever() - Callbacks:
addTimer,addRead - Non‑blocking: efficient I/O multiplexing
# Sum of array
proc sumArray[T: SomeNumber](arr: seq[T]): T =
result = 0
for num in arr:
result += num
echo sumArray(@[1, 2, 3, 4, 5]) # 15
# Using foldl
import sequtils
echo foldl(@[1, 2, 3, 4, 5], a + b, 0)asyncdispatch is the core module for asynchronous I/O, providing an event loop and async procs.
- Wait:
waitFor(someAsyncProc()) - Loop:
runForever() - Timers:
sleepAsync,addTimer
# Average of array
proc averageArray[T: SomeNumber](arr: seq[T]): float =
if arr.len == 0: return 0.0
var total: float = 0.0
for num in arr:
total += float(num)
return total / float(arr.len)
echo averageArray(@[1, 2, 3, 4, 5]) # 3.0
# Using sum and len
proc averageArray2[T: SomeNumber](arr: seq[T]): float =
if arr.len == 0: return 0.0
return float(foldl(arr, a + b, 0)) / float(arr.len)- refc: reference counting with cycle detection (old, uses GC)
- arc: automatic reference counting (no cycles, deterministic)
- orc: ARC with a cycle collector (recommended, default since 1.6)
# Sort array ascending
proc sortAscending[T](arr: seq[T]): seq[T] =
result = arr
result.sort()
echo sortAscending(@[5, 2, 8, 1, 9]) # [1, 2, 5, 8, 9]
# In-place
proc sortAscendingInplace[T](arr: var seq[T]) =
arr.sort()
# Custom sort key
proc sortByLength(strings: seq[string]): seq[string] =
result = strings
result.sort(proc(a, b: string): int = cmp(a.len, b.len))std/with provides a with template that can work with objects or resources, similar to Python’s context manager.
- Import:
import std/with - Usage:
with open("file"): work - Custom: define
enter/leavehooks
# Sort array descending
proc sortDescending[T](arr: seq[T]): seq[T] =
result = arr
result.sort(Descending)
echo sortDescending(@[5, 2, 8, 1, 9]) # [9, 8, 5, 2, 1]
# In-place
proc sortDescendingInplace[T](arr: var seq[T]) =
arr.sort(Descending)
# Custom sort by key descending
proc sortByKeyDesc(data: seq[tuple[key: int, val: string]], key: string): seq[tuple[key: int, val: string]] =
result = data
result.sort(proc(a, b: tuple[key: int, val: string]): int = cmp(b.key, a.key))let can be used inside blocks to create local immutable bindings that are not visible outside.
- Block:
block: let x = 10 - Scope: x exists only inside the block
# Flatten nested array (Nim doesn't have nested seqs easily, but we can use a variant)
# Using recursion with seq of some type
proc flattenArray(arr: seq[seq[int]]): seq[int] =
result = @[]
for sub in arr:
result.add(sub)
# For heterogeneous nesting, use a tagged union or a string representation.
# Using a generic flatten with varargs
proc flatten[T](arr: varargs[seq[T]]): seq[T] =
result = @[]
for sub in arr:
result.add(sub)
echo flatten(@[1, 2], @[3, 4]) # [1, 2, 3, 4]addr returns the memory address of a variable. It yields a ptr.
- Usage:
let p = addr(x) - Unsafe: use with care, mainly for low‑level code
# Chunk array
proc chunkArray[T](arr: seq[T], size: int): seq[seq[T]] =
result = @[]
var i = 0
while i < arr.len:
let chunkSize = min(size, arr.len - i)
result.add(arr[i..<i+chunkSize])
i += chunkSize
echo chunkArray(@[1, 2, 3, 4, 5, 6], 2) # [[1, 2], [3, 4], [5, 6]]
# Using slicing
proc chunkArraySlice[T](arr: seq[T], size: int): seq[seq[T]] =
result = @[]
for i in countup(0, arr.len-1, size):
let end = min(i+size, arr.len)
result.add(arr[i..<end])cast performs low‑level type casting without safety checks, e.g., converting between pointer types.
- Usage:
let p = cast[ptr int](addr(x)) - Unsafe: bypasses type system
# Binary search
proc binarySearch[T](arr: seq[T], target: T): int =
var left = 0
var right = arr.len - 1
while left <= right:
let mid = (left + right) div 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1
echo binarySearch(@[1, 2, 3, 4, 5, 6, 7], 5) # 4
# Using built-in binary search from algorithm
import std/algorithm
let arr = @[1, 2, 3, 4, 5, 6, 7]
let idx = arr.binarySearch(5) # returns index or -1- int: pointer‑sized, signed
- int8, int16, int32, int64
- uint, uint8, uint16, uint32, uint64
- BigInts: available via
std/bigints
# Quick sort
proc quickSort[T](arr: seq[T]): seq[T] =
if arr.len <= 1: return arr
let pivot = arr[0]
let left = arr.filterIt(it < pivot)
let right = arr.filterIt(it > pivot)
return quickSort(left) & @[pivot] & quickSort(right)
echo quickSort(@[5, 3, 8, 4, 2, 7, 1, 6])
# In-place quick sort
proc partition[T](arr: var seq[T], low, high: int): int =
let pivot = arr[high]
var i = low - 1
for j in low..<high:
if arr[j] <= pivot:
inc i
swap(arr[i], arr[j])
swap(arr[i+1], arr[high])
return i+1
proc quickSortInplace[T](arr: var seq[T], low = 0, high: int = -1) =
var h = if high == -1: arr.len - 1 else: high
if low < h:
let pi = partition(arr, low, h)
quickSortInplace(arr, low, pi - 1)
quickSortInplace(arr, pi + 1, h)- float: double precision (64‑bit)
- float32: single precision
- float64: same as float
# Merge sort
proc merge[T](left, right: seq[T]): seq[T] =
result = @[]
var i = 0; var j = 0
while i < left.len and j < right.len:
if left[i] <= right[j]:
result.add(left[i]); inc i
else:
result.add(right[j]); inc j
result.add(left[i..^1])
result.add(right[j..^1])
proc mergeSort[T](arr: seq[T]): seq[T] =
if arr.len <= 1: return arr
let mid = arr.len div 2
let left = mergeSort(arr[0..<mid])
let right = mergeSort(arr[mid..^1])
return merge(left, right)Nim supports bitwise operators: and, or, xor, not, shl, shr.
- Operators:
a and b,a shl 2 - Bit reversal:
bitnot(a)
# Bubble sort
proc bubbleSort[T](arr: seq[T]): seq[T] =
result = arr
for i in 0..<result.len-1:
for j in 0..<result.len-1-i:
if result[j] > result[j+1]:
swap(result[j], result[j+1])
# Optimized
proc bubbleSortOptimized[T](arr: seq[T]): seq[T] =
result = arr
for i in 0..<result.len-1:
var swapped = false
for j in 0..<result.len-1-i:
if result[j] > result[j+1]:
swap(result[j], result[j+1])
swapped = true
if not swapped: breakalgorithm provides sorting and searching routines, e.g., sort, binarySearch.
- Sort:
sort(mySeq) - Custom comparator:
sort(mySeq, cmp = proc(x, y: int): int = ...)
# Intersection of arrays
proc intersection[T](arr1, arr2: seq[T]): seq[T] =
let s1 = toHashSet(arr1)
let s2 = toHashSet(arr2)
result = @[]
for item in s1:
if item in s2:
result.add(item)
echo intersection(@[1, 2, 3, 4], @[3, 4, 5, 6]) # [3, 4]
# Using filter
proc intersectionFilter[T](arr1, arr2: seq[T]): seq[T] =
arr1.filterIt(it in arr2)sequtils provides functional operations on sequences: map, filter, foldl, zip, etc.
- Map:
let doubled = map(@[1,2,3], proc(x: int): int = x*2) - Filter:
let evens = filter(nums, proc(x: int): bool = x mod 2 == 0)
# Union of arrays
proc union[T](arr1, arr2: seq[T]): seq[T] =
var s = toHashSet(arr1)
s.incl(arr2)
return toSeq(s)
echo union(@[1, 2, 3], @[3, 4, 5]) # [1, 2, 3, 4, 5]
# Using concat and dedup
proc unionConcat[T](arr1, arr2: seq[T]): seq[T] =
let combined = arr1 & arr2
return combined.deduplicate()strutils offers many string utilities: toUpper, strip, split, join, replace, etc.
- Split:
"a,b,c".split(',') - Join:
@["a","b"].join("-")
# Difference of arrays
proc difference[T](arr1, arr2: seq[T]): seq[T] =
let s2 = toHashSet(arr2)
arr1.filterIt(it notin s2)
echo difference(@[1, 2, 3, 4], @[3, 4, 5, 6]) # [1, 2]
# Symmetric difference
proc symmetricDifference[T](arr1, arr2: seq[T]): seq[T] =
let s1 = toHashSet(arr1)
let s2 = toHashSet(arr2)
let diff1 = arr1.filterIt(it notin s2)
let diff2 = arr2.filterIt(it notin s1)
return diff1 & diff2json provides parsing and serialization of JSON data.
- Parse:
let data = parseJson("{\"key\":\"value\"}") - Access:
data["key"].getStr() - Generate:
%*{"name": "Nim"}.pretty()
# Group by property
import tables
type
Item = object
kind: string
name: string
proc groupByProperty(items: seq[Item], key: string): Table[string, seq[Item]] =
result = initTable[string, seq[Item]]()
for item in items:
let keyVal = case key
of "kind": item.kind
else: ""
if keyVal notin result:
result[keyVal] = @[]
result[keyVal].add(item)
var data = @[
Item(kind: "fruit", name: "apple"),
Item(kind: "fruit", name: "banana"),
Item(kind: "veg", name: "carrot")
]
let groups = groupByProperty(data, "kind")
for k, v in groups:
echo k, ": ", vparseutils provides low‑level parsing functions like parseInt, parseFloat, etc.
- Int:
var x: int; discard parseInt("123", x) - Float:
var y: float; discard parseFloat("3.14", y)
# Deep clone object
import std/objutils
# Using a generic deepCopy proc (not built-in, we'll implement a simple one)
proc deepClone[T](x: T): T =
# Works for simple types, but for refs and objects we need custom handling
return x
# For objects with references, we can use a custom procedure
type
Address = object
city: string
zip: string
User = ref object
name: string
address: Address
proc deepCloneUser(u: User): User =
result = User(name: u.name, address: u.address) # shallow copy for address
# Usage
var original = User(name: "Alice", address: Address(city: "NYC", zip: "10001"))
var cloned = deepCloneUser(original)
cloned.name = "Bob"
echo original.name # Alice
echo cloned.name # BobSee Q43 for basic regex usage. std/re provides full regex support with capture groups, replacements, and flags.
- Match:
let m = match(s, re"(\d+)") - Replace:
replace(s, re"[aeiou]", "*")
# Immutable update (using copy and modify)
type
User = object
name: string
age: int
State = object
user: User
proc updateImmutable(state: State, path: string, value: int): State =
# This is a simple example; for nested paths, we'd need a more complex approach
case path
of "user.age":
let newUser = User(name: state.user.name, age: value)
return State(user: newUser)
else:
return state
let state = State(user: User(name: "Alice", age: 25))
let newState = updateImmutable(state, "user.age", 26)
echo state.user.age # 25
echo newState.user.age # 26uri provides parsing and construction of URIs/URLs.
- Parse:
let u = parseUri("https://nim-lang.org/") - Access:
u.scheme,u.hostname
# Pipe function (using a template)
template pipe(value, body: untyped): untyped =
var result = value
body
result
# Usage
proc double(x: int): int = x * 2
proc addTen(x: int): int = x + 10
proc square(x: int): int = x * x
let result = pipe(5):
result = double(result)
result = addTen(result)
result = square(result)
echo result # 400
# Alternatively, using proc composition
proc compose[A,B,C](f: proc(x: B): C, g: proc(x: A): B): proc(x: A): C =
return proc(x: A): C = f(g(x))
let process = compose(square, compose(addTen, double))
echo process(5) # 400httpclient allows making HTTP requests synchronously or asynchronously.
- Get:
let client = newHttpClient(); let resp = client.get("https://example.com") - Async:
proc asyncGet() {.async.} = await client.get(...)
# Compose function (reverse of pipe)
proc composeReverse[A,B,C](f: proc(x: B): C, g: proc(x: A): B): proc(x: A): C =
return proc(x: A): C = f(g(x))
proc double(x: int): int = x * 2
proc addTen(x: int): int = x + 10
proc square(x: int): int = x * x
let process = composeReverse(square, composeReverse(addTen, double))
echo process(5) # (5*2+10)^2 = 400asyncfile provides asynchronous file I/O.
- Open:
let f = openAsync("file.txt", fmRead) - Read:
let data = await f.readAll()
# Memoization (using a table)
import tables
proc memoize[T, R](f: proc(x: T): R): proc(x: T): R =
var cache = initTable[T, R]()
return proc(x: T): R =
if x in cache:
return cache[x]
else:
let result = f(x)
cache[x] = result
return result
proc fib(n: int): int =
if n <= 1: n
else: fib(n-1) + fib(n-2)
let memoFib = memoize(fib)
echo memoFib(10)
# Using a built-in caching macro? Not standard, but we can use a closure.when is a compile‑time conditional similar to if, but evaluated during compilation.
- Usage:
when defined(windows): ... - Static: branches that are not taken are not compiled
# Once function (using a global flag)
proc once(f: proc(): string): proc(): string =
var called = false
var result: string
return proc(): string =
if not called:
called = true
result = f()
return result
proc initialize(): string =
echo "Initialized"
return "App initialized"
let initOnce = once(initialize)
echo initOnce() # prints "Initialized" and "App initialized"
echo initOnce() # prints "App initialized" again, no re-initstatic: blocks execute code at compile time. Useful for initializing compile‑time data.
- Syntax:
static: echo "Compile time" - Result: can define constants
# Debounce with leading edge (not built-in, but can use a timer)
import std/asyncdispatch
import std/times
proc debounceLeading(delayMs: int, action: proc()) {.async.} =
var lastCall = 0
while true:
let now = cpuTime() * 1000
if now - lastCall >= delayMs:
lastCall = now
action()
await sleepAsync(1)
# Usage
proc printHello() = echo "Hello"
# This would run in an async loop; not a simple function, so we skip practical usage.{.compileTime.} marks a procedure to be executed at compile time, enabling CTFE.
- Mark:
proc compute(): int {.compileTime.} = ... - Use:
const x = compute()
# Throttle with leading edge (similar to debounce)
proc throttleLeading(delayMs: int, action: proc()) {.async.} =
var lastCall = 0
while true:
let now = cpuTime() * 1000
if now - lastCall >= delayMs:
lastCall = now
action()
await sleepAsync(1)
# Usage would be similar to debounce.Nim allows creating custom literal suffixes using the suffix pragma.
- Define:
proc \`"kg"\`(x: int): int = x * 1000 - Usage:
5.kg
# Deep equal (using a recursive proc)
proc deepEqual(a, b: any): bool =
# This is a simplified version; for complex types, we need more.
if a.type != b.type: return false
when a.type is string or a.type is int or a.type is float:
return a == b
elif a.type is seq:
if a.len != b.len: return false
for i in 0..<a.len:
if not deepEqual(a[i], b[i]): return false
return true
elif a.type is Table:
if a.len != b.len: return false
for key in keys(a):
if not deepEqual(a[key], b[key]): return false
return true
elif a.type is object:
for field in fields(a):
if not deepEqual(a.field, b.field): return false
return true
else:
return a == b
# Usage
let obj1 = (name: "Alice", address: (city: "NYC"))
let obj2 = (name: "Alice", address: (city: "NYC"))
echo deepEqual(obj1, obj2) # trueTraits can be emulated using concepts or via type classes and overloading. You can also use the std/typetraits module.
- Concepts:
type MyConcept = concept - Type traits:
std/typetraitsprovides compile‑time reflection
# Observable pattern
type
Observer = proc(data: string)
Observable = object
subscribers: seq[Observer]
proc newObservable(): Observable =
Observable(subscribers: @[])
proc subscribe(self: var Observable, callback: Observer): proc() =
self.subscribers.add(callback)
return proc() =
let idx = self.subscribers.find(callback)
if idx != -1:
self.subscribers.delete(idx)
proc notify(self: Observable, data: string) =
for cb in self.subscribers:
cb(data)
# Usage
var observable = newObservable()
let unsubscribe = subscribe(observable, proc(data: string) = echo "Received: ", data)
notify(observable, "Hello") # Received: Hello
unsubscribe()
notify(observable, "World") # nothingConcepts define compile‑time constraints on generic types.
- Define:
type Addable = concept x, y - Use:
proc add[T: Addable](a, b: T): T
# Singleton pattern (using a global variable)
type
Singleton = object
data: Table[string, string]
var instance: Singleton
proc getInstance(): var Singleton =
if instance.data.isNil:
instance = Singleton(data: initTable[string, string]())
return instance
proc setVal(self: var Singleton, key, value: string) =
self.data[key] = value
proc getVal(self: Singleton, key: string): string =
return self.data.getOrDefault(key)
# Usage
var s1 = getInstance()
setVal(s1, "name", "Alice")
var s2 = getInstance()
echo getVal(s2, "name") # AliceUse the reverse proc from std/algorithm or manual loop.
- Using algorithm:
reverse(s)(mutates) - Manual: iterate from end to start
# Factory pattern
type
UserKind = enum
admin, guest, regular
User = ref object of RootObj
Admin = ref object of User
Guest = ref object of User
Regular = ref object of User
proc getRole(u: User): string =
if u of Admin: return "admin"
elif u of Guest: return "guest"
else: return "regular"
proc createUser(kind: UserKind): User =
case kind
of admin: return Admin()
of guest: return Guest()
of regular: return Regular()
# Usage
let adminUser = createUser(admin)
echo getRole(adminUser) # adminCompare string with its reverse.
- Reverse:
s == reversed(s) - Two‑pointer: O(n) time, O(1) space
# Strategy pattern
type
PaymentStrategy = proc(amount: float)
proc creditCardPay(amount: float) =
echo "Paid $", amount, " with Credit Card"
proc payPalPay(amount: float) =
echo "Paid $", amount, " with PayPal"
proc cryptoPay(amount: float) =
echo "Paid $", amount, " with Crypto"
type
PaymentContext = object
strategy: PaymentStrategy
proc newPaymentContext(strategy: PaymentStrategy): PaymentContext =
PaymentContext(strategy: strategy)
proc setStrategy(self: var PaymentContext, strategy: PaymentStrategy) =
self.strategy = strategy
proc executePayment(self: PaymentContext, amount: float) =
self.strategy(amount)
# Usage
var context = newPaymentContext(creditCardPay)
executePayment(context, 100)
setStrategy(context, payPalPay)
executePayment(context, 50)Recursive function with base case.
# Observer pattern (similar to Q79 but with classes)
type
Observer = ref object of RootObj
name: string
Subject = ref object of RootObj
observers: seq[Observer]
state: string
method update(o: Observer, data: string) {.base.} =
echo o.name, " received: ", data
proc newObserver(name: string): Observer =
Observer(name: name)
proc newSubject(): Subject =
Subject(observers: @[])
proc attach(s: Subject, o: Observer) =
s.observers.add(o)
proc detach(s: Subject, o: Observer) =
let idx = s.observers.find(o)
if idx != -1:
s.observers.delete(idx)
proc notify(s: Subject) =
for o in s.observers:
update(o, s.state)
proc setState(s: Subject, newState: string) =
s.state = newState
notify(s)
# Usage
let subject = newSubject()
let o1 = newObserver("Observer1")
let o2 = newObserver("Observer2")
attach(subject, o1)
attach(subject, o2)
setState(subject, "Hello World")Iterative or recursive. Use memoization for efficiency.
# Decorator pattern (using composition)
type
Coffee = object
cost: float
description: string
proc milkDecorator(c: Coffee): Coffee =
result = Coffee(cost: c.cost + 2.0, description: c.description & ", Milk")
proc sugarDecorator(c: Coffee): Coffee =
result = Coffee(cost: c.cost + 1.0, description: c.description & ", Sugar")
# Usage
var coffee = Coffee(cost: 5.0, description: "Coffee")
coffee = milkDecorator(coffee)
coffee = sugarDecorator(coffee)
echo coffee.description # Coffee, Milk, Sugar
echo coffee.cost # 8.0
# Class-based decorator
type
CoffeeDecorator = ref object
coffee: Coffee
proc cost(d: CoffeeDecorator): float = d.coffee.cost
proc description(d: CoffeeDecorator): string = d.coffee.description
proc newMilkDecorator(c: Coffee): CoffeeDecorator =
result = CoffeeDecorator(coffee: c)
result.coffee.cost += 2.0
result.coffee.description &= ", Milk"
proc newSugarDecorator(c: Coffee): CoffeeDecorator =
result = CoffeeDecorator(coffee: c)
result.coffee.cost += 1.0
result.coffee.description &= ", Sugar"Search sorted array for a target.
# Command pattern
type
Command = ref object of RootObj
AddCommand = ref object of Command
receiver: seq[int]
value: int
method execute(c: Command) {.base.} = discard
method undo(c: Command) {.base.} = discard
method execute(c: AddCommand) =
c.receiver.add(c.value)
method undo(c: AddCommand) =
let idx = c.receiver.find(c.value)
if idx != -1:
c.receiver.delete(idx)
# Usage
var receiver = @[1, 2, 3]
var cmd = AddCommand(receiver: receiver, value: 4)
execute(cmd)
echo receiver # [1, 2, 3, 4]
undo(cmd)
echo receiver # [1, 2, 3]
# Command manager for undo/redo
type
CommandManager = object
history: seq[Command]
redoStack: seq[Command]
proc execute(cm: var CommandManager, c: Command) =
execute(c)
cm.history.add(c)
cm.redoStack.setLen(0)
proc undo(cm: var CommandManager) =
if cm.history.len > 0:
let c = cm.history.pop()
undo(c)
cm.redoStack.add(c)
proc redo(cm: var CommandManager) =
if cm.redoStack.len > 0:
let c = cm.redoStack.pop()
execute(c)
cm.history.add(c)Recursive divide‑and‑conquer sorting.
# Memento pattern
type
Memento = object
state: string
Originator = object
state: string
proc saveState(o: Originator): Memento =
Memento(state: o.state)
proc restoreState(o: var Originator, m: Memento) =
o.state = m.state
type
Caretaker = object
mementos: seq[Memento]
proc addMemento(c: var Caretaker, m: Memento) =
c.mementos.add(m)
proc getMemento(c: Caretaker, idx: int): Memento =
return c.mementos[idx]
# Usage
var originator = Originator(state: "State 1")
var caretaker = Caretaker()
addMemento(caretaker, saveState(originator))
originator.state = "State 2"
addMemento(caretaker, saveState(originator))
originator.state = "State 3"
restoreState(originator, getMemento(caretaker, 0))
echo originator.state # State 1Recursive merge sort.
# Mediator pattern
type
Mediator = ref object of RootObj
colleagues: seq[Colleague]
Colleague = ref object of RootObj
name: string
mediator: Mediator
method receive(c: Colleague, msg: string) {.base.} =
echo c.name, " received: ", msg
proc send(c: Colleague, msg: string) =
if c.mediator != nil:
for col in c.mediator.colleagues:
if col != c:
receive(col, msg)
proc register(m: Mediator, c: Colleague) =
m.colleagues.add(c)
c.mediator = m
# Usage
var mediator = Mediator()
var alice = Colleague(name: "Alice")
var bob = Colleague(name: "Bob")
register(mediator, alice)
register(mediator, bob)
send(alice, "Hello Bob!") # Bob receives
# Chat room
type
ChatRoom = ref object of Mediator
history: seq[string]
method receive(c: Colleague, msg: string) {.base.} =
echo c.name, " received: ", msg
# Override send to log
proc sendChat(c: Colleague, msg: string) =
if c.mediator != nil:
# Log message
for col in c.mediator.colleagues:
if col != c:
receive(col, msg)Heap sort using a binary heap.
# Chain of Responsibility
type
Handler = ref object of RootObj
next: Handler
method handle(h: Handler, request: Table[string, string]): bool {.base.} =
if h.next != nil:
return h.next.handle(request)
else:
return false
type
AuthHandler = ref object of Handler
method handle(h: AuthHandler, request: Table[string, string]): bool =
if request.hasKey("token"):
echo "Authentication passed"
return procCall handle(Handler(h), request)
else:
echo "Authentication failed"
return false
type
LoggerHandler = ref object of Handler
method handle(h: LoggerHandler, request: Table[string, string]): bool =
echo "Logging request: ", request.getOrDefault("url")
return procCall handle(Handler(h), request)
type
PermissionHandler = ref object of Handler
method handle(h: PermissionHandler, request: Table[string, string]): bool =
if request.getOrDefault("permissions").contains("read"):
echo "Permission granted"
return procCall handle(Handler(h), request)
else:
echo "Permission denied"
return false
# Usage
var auth = AuthHandler()
var logger = LoggerHandler()
var perm = PermissionHandler()
auth.next = logger
logger.next = perm
var request = {"token": "valid", "url": "/api/data", "permissions": "read"}.toTable
discard handle(auth, request)Define a ref object for nodes and reverse iteratively.
# State pattern
type
Context = ref object
state: State
State = ref object of RootObj
method handle(s: State, ctx: Context) {.base.} = discard
type
ReadyState = ref object of State
ProcessingState = ref object of State
CompletedState = ref object of State
method handle(s: ReadyState, ctx: Context) =
echo "Ready: Waiting for input"
ctx.state = ProcessingState()
method handle(s: ProcessingState, ctx: Context) =
echo "Processing: Working on task"
ctx.state = CompletedState()
method handle(s: CompletedState, ctx: Context) =
echo "Completed: Task finished"
# Usage
var ctx = Context(state: ReadyState())
handle(ctx.state, ctx)
handle(ctx.state, ctx)
handle(ctx.state, ctx)Use Floyd’s cycle detection (tortoise and hare).
# Proxy pattern
type
Subject = ref object of RootObj
method request(s: Subject) {.base.} = discard
type
RealSubject = ref object of Subject
method request(s: RealSubject) =
echo "RealSubject: Handling request"
type
Proxy = ref object of Subject
real: RealSubject
method request(p: Proxy) =
if p.real == nil:
echo "Proxy: Creating real subject"
p.real = RealSubject()
echo "Proxy: Checking access"
request(p.real)
# Usage
var proxy = Proxy()
proxy.request()Find two numbers that add to target.
# Flyweight pattern
type
Flyweight = object
sharedState: string
proc newFlyweight(shared: string): Flyweight =
Flyweight(sharedState: shared)
proc operation(f: Flyweight, uniqueState: string) =
echo "Shared: ", f.sharedState, ", Unique: ", uniqueState
type
FlyweightFactory = object
flyweights: Table[string, Flyweight]
proc getFlyweight(factory: var FlyweightFactory, shared: string): Flyweight =
if shared notin factory.flyweights:
factory.flyweights[shared] = newFlyweight(shared)
echo "Creating new flyweight for: ", shared
return factory.flyweights[shared]
# Usage
var factory = FlyweightFactory()
let fw1 = getFlyweight(factory, "state1")
let fw2 = getFlyweight(factory, "state1")
let fw3 = getFlyweight(factory, "state2")
fw1.operation("unique1")
fw2.operation("unique2")
fw3.operation("unique3")Kadane’s algorithm for maximum contiguous subarray sum.
# Bridge pattern
type
Implementation = ref object of RootObj
method operationImpl(i: Implementation) {.base.} = discard
type
ConcreteImplA = ref object of Implementation
ConcreteImplB = ref object of Implementation
method operationImpl(i: ConcreteImplA) =
echo "ConcreteImplA: Operation"
method operationImpl(i: ConcreteImplB) =
echo "ConcreteImplB: Operation"
type
Abstraction = ref object of RootObj
impl: Implementation
proc newAbstraction(impl: Implementation): Abstraction =
Abstraction(impl: impl)
method operation(a: Abstraction) =
echo "Abstraction: Additional logic"
operationImpl(a.impl)
# Extended abstraction
type
ExtendedAbstraction = ref object of Abstraction
method operation(e: ExtendedAbstraction) =
echo "ExtendedAbstraction: More logic"
procCall operation(Abstraction(e))
# Usage
let implA = ConcreteImplA()
let implB = ConcreteImplB()
let ab1 = newAbstraction(implA)
let ab2 = newAbstraction(implB)
operation(ab1)
operation(ab2)DP solution for LCS.
# Adapter pattern
type
Target = ref object of RootObj
method request(t: Target) {.base.} = discard
type
Adaptee = ref object of RootObj
method specificRequest(a: Adaptee) {.base.} = discard
type
Adapter = ref object of Target
adaptee: Adaptee
method request(a: Adapter) =
specificRequest(a.adaptee)
# Usage
let adaptee = Adaptee()
let adapter = Adapter(adaptee: adaptee)
request(adapter)
# Object adapter
type
ObjectAdapter = ref object
adaptee: Adaptee
proc request(o: ObjectAdapter) =
specificRequest(o.adaptee)
# Class adapter (using inheritance)
type
ClassAdapter = ref object of Adaptee, Target # multiple inheritance not supported, but we can compose
# Instead, use composition and forward.0/1 knapsack DP.
# Facade pattern
type
SubsystemA = object
SubsystemB = object
SubsystemC = object
proc operationA(s: SubsystemA) = echo "SubsystemA: Operation"
proc operationB(s: SubsystemB) = echo "SubsystemB: Operation"
proc operationC(s: SubsystemC) = echo "SubsystemC: Operation"
type
Facade = object
a: SubsystemA
b: SubsystemB
c: SubsystemC
proc newFacade(): Facade =
Facade(a: SubsystemA(), b: SubsystemB(), c: SubsystemC())
proc operation(f: Facade) =
echo "Facade: Complex operation"
operationA(f.a)
operationB(f.b)
operationC(f.c)
proc simplifiedOperation(f: Facade) =
echo "Facade: Simplified operation"
operationA(f.a)
# Usage
let facade = newFacade()
operation(facade)
simplifiedOperation(facade)Place N queens on an N×N board.
# Composite pattern
type
Component = ref object of RootObj
method operation(c: Component) {.base.} = discard
type
Leaf = ref object of Component
name: string
method operation(l: Leaf) =
echo "Leaf ", l.name, ": Operation"
type
Composite = ref object of Component
name: string
children: seq[Component]
method operation(c: Composite) =
echo "Composite ", c.name, ": Operation"
for child in c.children:
operation(child)
proc add(c: Composite, child: Component) =
c.children.add(child)
proc remove(c: Composite, child: Component) =
let idx = c.children.find(child)
if idx != -1:
c.children.delete(idx)
# Usage
let leaf1 = Leaf(name: "A")
let leaf2 = Leaf(name: "B")
let composite = Composite(name: "Root")
add(composite, leaf1)
add(composite, leaf2)
operation(composite)Backtracking solver for 9x9 Sudoku.
# Visitor pattern
type
Visitor = ref object of RootObj
Element = ref object of RootObj
method visitElementA(v: Visitor, e: Element) {.base.} = discard
method visitElementB(v: Visitor, e: Element) {.base.} = discard
method accept(e: Element, v: Visitor) {.base.} = discard
type
ElementA = ref object of Element
ElementB = ref object of Element
method accept(e: ElementA, v: Visitor) =
visitElementA(v, e)
method accept(e: ElementB, v: Visitor) =
visitElementB(v, e)
type
ConcreteVisitor = ref object of Visitor
method visitElementA(v: ConcreteVisitor, e: Element) =
echo "Visiting ElementA"
method visitElementB(v: ConcreteVisitor, e: Element) =
echo "Visiting ElementB"
# Usage
let visitor = ConcreteVisitor()
let elemA = ElementA()
let elemB = ElementB()
accept(elemA, visitor)
accept(elemB, visitor)Merge into a new sorted sequence.
# Iterator pattern (using Nim's built-in iterators)
# We already have iterators. This is just an example of custom iterator.
iterator myRange(start, stop: int): int =
for i in start..stop:
yield i
# Usage
for i in myRange(1, 5):
echo i
# Custom collection with iterator
type
MyCollection = object
items: seq[string]
proc add(c: var MyCollection, item: string) =
c.items.add(item)
iterator items(c: MyCollection): string =
for item in c.items:
yield item
# Usage
var c = MyCollection()
add(c, "A"); add(c, "B"); add(c, "C")
for item in c.items:
echo itemGiven array 0..n with one missing, find it using XOR or sum.
# Template Method pattern
type
AbstractClass = ref object of RootObj
method step1(a: AbstractClass) = echo "Step 1"
method step2(a: AbstractClass) {.base.} = discard
method step3(a: AbstractClass) = echo "Step 3"
method templateMethod(a: AbstractClass) =
step1(a)
step2(a)
step3(a)
type
ConcreteClass = ref object of AbstractClass
method step2(c: ConcreteClass) =
echo "Concrete Step 2"
# Usage
let concrete = ConcreteClass()
templateMethod(concrete)Use a stack.
# Builder pattern
type
Product = object
parts: seq[string]
proc add(p: var Product, part: string) =
p.parts.add(part)
proc listParts(p: Product) =
echo p.parts.join(", ")
type
Builder = ref object
product: Product
proc newBuilder(): Builder =
Builder(product: Product())
proc reset(b: Builder) =
b.product = Product()
proc buildStepA(b: Builder) =
b.product.add("Part A")
proc buildStepB(b: Builder) =
b.product.add("Part B")
proc getResult(b: Builder): Product =
return b.product
type
Director = ref object
builder: Builder
proc newDirector(b: Builder): Director =
Director(builder: b)
proc buildMinimal(d: Director) =
d.builder.buildStepA()
proc buildFull(d: Director) =
d.builder.buildStepA()
d.builder.buildStepB()
# Usage
let builder = newBuilder()
let director = newDirector(builder)
director.buildMinimal()
let product = builder.getResult()
product.listParts() # Part A
# Fluent builder
type
UserBuilder = object
name: string
age: int
email: string
proc name(b: var UserBuilder, n: string): var UserBuilder = b.name = n; b
proc age(b: var UserBuilder, a: int): var UserBuilder = b.age = a; b
proc email(b: var UserBuilder, e: string): var UserBuilder = b.email = e; b
proc build(b: UserBuilder): tuple[name: string, age: int, email: string] =
(name: b.name, age: b.age, email: b.email)
# Usage
let user = UserBuilder().name("Alice").age(25).email("alice@example.com").build()
echo userUse a sequence as underlying storage.
# Prototype pattern
import std/copy
type
Prototype = object
name: string
nested: Table[string, int]
proc clone(p: Prototype): Prototype =
result = p
# shallow copy of nested (needs deep copy)
result.nested = p.nested
proc deepClone(p: Prototype): Prototype =
result = p
# deep copy of nested
result.nested = deepCopy(p.nested)
# Usage
var original = Prototype(name: "Original", nested: {"value": 42}.toTable)
var copyObj = original.clone()
copyObj.name = "Copy"
copyObj.nested["value"] = 99
echo original.name # Original
echo original.nested # {value: 42} (shallow copy)
var deepCopyObj = original.deepClone()
deepCopyObj.nested["value"] = 100
echo original.nested # {value: 42} (deep copy)
# Registry pattern
type
PrototypeRegistry = object
prototypes: Table[string, Prototype]
proc register(r: var PrototypeRegistry, key: string, p: Prototype) =
r.prototypes[key] = p
proc get(r: PrototypeRegistry, key: string): Prototype =
return r.prototypes[key].clone()
# Usage
var registry = PrototypeRegistry()
registry.register("user", Prototype(name: "User", nested: {"name": 0}.toTable))
var user = registry.get("user")
user.name = "Alice"
echo user.nameFrequently Asked Questions
For common doubts about Nim, refer to the official documentation or community resources.