InterviewPitch
Nim interview questions

Nim Interview Questions with Answers

Most Asked Nim Interview Questions for Software Engineer Roles

100+ QuestionsDetailed AnswersCode ExamplesUpdated for 2026

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

Beginner
1. What is Nim?

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
Nim
# Hello World in Nim
echo "Hello, World!"

# Using a procedure
proc greet(): string =
  return "Hello, World!"
echo greet()
Beginner
2. How to install Nim?

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
Nim
# 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 CONSTANT
Beginner
3. How to declare variables in Nim?

Nim 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"
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  # true
Beginner
4. What are the data types in Nim?

Nim 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
Nim
# 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)
Beginner
5. How to define procedures in Nim?

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 value or implicit last expression
  • Lambda: let f = proc(x: int): int = x * 2
Nim
# 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_val
Beginner
6. What are sequences in Nim?

Sequences (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)
Nim
# 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_val
Beginner
7. What are tuples in Nim?

Tuples 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] or t.x
  • Assignment: var (a, b) = (1, 2)
  • Unpacking: let (x, y) = getPoint()
Nim
# 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 person1
Beginner
8. What are arrays in Nim?

Arrays 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]]
Nim
# 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()
Beginner
9. What are sets in Nim?

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
Nim
# 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)
Beginner
10. What are tables in Nim?

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
Nim
# 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, ": ", item
Beginner
11. How to use if‑else in Nim?

Nim 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
Nim
# 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.name
Beginner
12. How to use for loops in Nim?

for 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
Nim
# 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)
Beginner
13. How to use while loops in Nim?

while loops execute as long as a condition holds.

  • Syntax: while condition: ...
  • Break: break exits the loop
  • Continue: continue skips to next iteration
Nim
# 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] = instances
Beginner
14. How to use case statements in Nim?

case 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
Nim
# 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()
Beginner
15. What are objects in Nim?

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
Nim
# 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 mapped
Beginner
16. How to define methods in Nim?

Methods 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
Nim
# 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"
Beginner
17. Difference between let, var, and const?
  • var: mutable variable, evaluated at runtime
  • let: immutable variable, evaluated at runtime
  • const: compile‑time constant, must be known at compile time
Nim
# 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)
Beginner
18. What is type inference in Nim?

Nim infers types from the initial value, so explicit annotations are often optional.

  • Variable: let x = 42int
  • Procedure return: inferred from last expression
  • Generic types: inferred from usage
Nim
# 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"].name
Beginner
19. How to import modules in Nim?

Use 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
Nim
# 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
Beginner
20. What are comments in Nim?
  • Line comment: # comment
  • Block comment: #[ block comment ]#
  • Documentation: ## doc comment (for nim doc)
Nim
# 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 sumAll
Intermediate
21. What are generics in Nim?

Generics 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)
Nim
# 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()
Intermediate
22. What are templates in Nim?

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
Nim
# 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)
Intermediate
23. What are macros in Nim?

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
Nim
# 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()
Intermediate
24. How to handle exceptions in Nim?

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")
Nim
# 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))
Intermediate
25. What is the concept of 'nil' in Nim?

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 Option for safety
Nim
# 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])
Intermediate
26. How to use options and result types?

Nim provides Option[T] (in std/options) for optional values and Result[T, E] for fallible operations.

  • Option: some(value) or none(T)
  • Result: ok(value) or err(error)
  • Use: safe handling of nulls and errors
Nim
# 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
Intermediate
27. Difference between ref and ptr?
  • ref: garbage‑collected or traced pointer; safe, managed
  • ptr: unmanaged pointer; unsafe, for low‑level code
  • Memory: ref points to objects on heap (GC), ptr can point anywhere
Nim
# 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.apiUrl
Intermediate
28. How to do memory management in Nim?

Nim 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, dealloc from system
Nim
# 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 sql
Intermediate
29. What is the garbage collector in Nim?

Nim’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
Nim
# 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.name
Intermediate
30. How to use the 'new' keyword?

new 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)
Nim
# 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)
Intermediate
31. What are iterators in Nim?

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
Nim
# 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"
Intermediate
32. What are closures in Nim?

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
Nim
# 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()
Intermediate
33. How to use async/await in Nim?

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())
Nim
# 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 = 30
Intermediate
34. What are calling conventions?

Calling 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
Nim
# 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()
Intermediate
35. How to use FFI in Nim?

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.}
Nim
# 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"
Intermediate
36. What are pragmas in Nim?

Pragmas are compiler directives or annotations that control code generation, warnings, and linking.

  • Examples: {.inline.}, {.noSideEffect.}, {.deprecated.}
  • Link: {.passL: "-lssl".}
  • Export: {.exportc.}
Nim
# 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()
Intermediate
37. What are distinct types?

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
Nim
# 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()
Intermediate
38. How to use type aliases?

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
Nim
# 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()
Intermediate
39. What are enumeration types?

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
Nim
# 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()
Intermediate
40. What is the 'discard' statement?

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
# 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.name
Intermediate
41. How to work with strings in Nim?

Nim strings are mutable, 0‑based, and support concatenation, slicing, and many utilities.

  • Concat: & or add
  • Slicing: s[0..3]
  • Length: len(s)
  • Search: find(s, "sub")
Nim
# 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")
Intermediate
42. How to work with files?

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)
Nim
# 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 true
Intermediate
43. What are regular expressions?

Nim 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+)")
Nim
# 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])
Intermediate
44. How to use the 'system' module?

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
Nim
# 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)
Intermediate
45. How to use the 'math' module?

math provides mathematical functions: trigonometric, exponential, logarithmic, etc.

  • Import: import math
  • Functions: sqrt, sin, cos, pow, ln
  • Constants: PI, E
Nim
# 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)
Intermediate
46. How to use the 'times' module?

times provides date, time, and duration handling.

  • Now: let now = now()
  • Format: now.format("yyyy-MM-dd")
  • Duration: var d = initDuration(minutes=5)
Nim
# 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)
Intermediate
47. How to use the 'random' module?

random provides random number generation.

  • Seed: randomize()
  • Rand: rand(100) (0..100)
  • Float: rand(1.0)
Nim
# 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)
Intermediate
48. How to create a Nimble package?

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
Nim
# 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)
Intermediate
49. What is compile‑time execution?

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
Nim
# 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")
Intermediate
50. What are concepts in Nim?

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
Nim
# 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()
Advanced
51. What is metaprogramming in Nim?

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
Nim
# 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 own
Advanced
52. How to create DSLs in Nim?

DSLs 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
Nim
# 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)
Advanced
53. What are effect systems?

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
Nim
# 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)
Advanced
54. How to use thread‑local storage?

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
Nim
# 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 xorSum
Advanced
55. How to use channels for concurrency?

Channels (from std/channels) enable safe communication between threads.

  • Create: var chan: Channel[int]
  • Send: chan.send(42)
  • Receive: let x = chan.recv()
Nim
# 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)
Advanced
56. What is the reactor pattern?

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
Nim
# 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)
Advanced
57. How to use 'asyncdispatch'?

asyncdispatch is the core module for asynchronous I/O, providing an event loop and async procs.

  • Wait: waitFor(someAsyncProc())
  • Loop: runForever()
  • Timers: sleepAsync, addTimer
Nim
# 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)
Advanced
58. Memory management strategies: refc, arc, orc?
  • 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)
Nim
# 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))
Advanced
59. How to use 'std/with' template?

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/leave hooks
Nim
# 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))
Advanced
60. What is 'let' with block scope?

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
Nim
# 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]
Advanced
61. How to use the 'addr' operator?

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
Nim
# 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])
Advanced
62. How to use the 'cast' operator?

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
Nim
# 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
Advanced
63. What are integer types in Nim?
  • int: pointer‑sized, signed
  • int8, int16, int32, int64
  • uint, uint8, uint16, uint32, uint64
  • BigInts: available via std/bigints
Nim
# 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)
Advanced
64. What are floating‑point types?
  • float: double precision (64‑bit)
  • float32: single precision
  • float64: same as float
Nim
# 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)
Advanced
65. How to do bit operations?

Nim supports bitwise operators: and, or, xor, not, shl, shr.

  • Operators: a and b, a shl 2
  • Bit reversal: bitnot(a)
Nim
# 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: break
Advanced
66. How to use 'std/algorithm'?

algorithm provides sorting and searching routines, e.g., sort, binarySearch.

  • Sort: sort(mySeq)
  • Custom comparator: sort(mySeq, cmp = proc(x, y: int): int = ...)
Nim
# 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)
Advanced
67. How to use 'std/sequtils'?

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)
Nim
# 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()
Advanced
68. How to use 'std/strutils'?

strutils offers many string utilities: toUpper, strip, split, join, replace, etc.

  • Split: "a,b,c".split(',')
  • Join: @["a","b"].join("-")
Nim
# 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 & diff2
Advanced
69. How to use 'std/json'?

json provides parsing and serialization of JSON data.

  • Parse: let data = parseJson("{\"key\":\"value\"}")
  • Access: data["key"].getStr()
  • Generate: %*{"name": "Nim"}.pretty()
Nim
# 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, ": ", v
Advanced
70. How to use 'std/parseutils'?

parseutils 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)
Nim
# 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    # Bob
Advanced
71. How to use 'std/re' for regex?

See 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]", "*")
Nim
# 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   # 26
Advanced
72. How to use 'std/uri'?

uri provides parsing and construction of URIs/URLs.

  • Parse: let u = parseUri("https://nim-lang.org/")
  • Access: u.scheme, u.hostname
Nim
# 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)  # 400
Advanced
73. How to use 'std/httpclient'?

httpclient 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(...)
Nim
# 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 = 400
Advanced
74. How to use 'std/asyncfile'?

asyncfile provides asynchronous file I/O.

  • Open: let f = openAsync("file.txt", fmRead)
  • Read: let data = await f.readAll()
Nim
# 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.
Advanced
75. What are 'when' statements?

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
Nim
# 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-init
Advanced
76. What are 'static' blocks?

static: blocks execute code at compile time. Useful for initializing compile‑time data.

  • Syntax: static: echo "Compile time"
  • Result: can define constants
Nim
# 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.
Advanced
77. How to use 'compileTime' pragmas?

{.compileTime.} marks a procedure to be executed at compile time, enabling CTFE.

  • Mark: proc compute(): int {.compileTime.} = ...
  • Use: const x = compute()
Nim
# 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.
Advanced
78. What are user‑defined literals?

Nim allows creating custom literal suffixes using the suffix pragma.

  • Define: proc \`"kg"\`(x: int): int = x * 1000
  • Usage: 5.kg
Nim
# 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)  # true
Advanced
79. How to implement traits?

Traits 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/typetraits provides compile‑time reflection
Nim
# 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")  # nothing
Advanced
80. How to use 'concept' for constraints?

Concepts define compile‑time constraints on generic types.

  • Define: type Addable = concept x, y
  • Use: proc add[T: Addable](a, b: T): T
Nim
# 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")  # Alice
Coding Round
81. Reverse a string in Nim

Use the reverse proc from std/algorithm or manual loop.

  • Using algorithm: reverse(s) (mutates)
  • Manual: iterate from end to start
Nim
# 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)  # admin
Coding Round
82. Check if a string is palindrome

Compare string with its reverse.

  • Reverse: s == reversed(s)
  • Two‑pointer: O(n) time, O(1) space
Nim
# 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)
Coding Round
83. Factorial using recursion

Recursive function with base case.

Nim
# 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")
Coding Round
84. Fibonacci series

Iterative or recursive. Use memoization for efficiency.

Nim
# 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"
Coding Round
86. Quick sort

Recursive divide‑and‑conquer sorting.

Nim
# 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 1
Coding Round
87. Merge sort

Recursive merge sort.

Nim
# 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)
Coding Round
88. Heap sort

Heap sort using a binary heap.

Nim
# 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)
Coding Round
89. Reverse a linked list

Define a ref object for nodes and reverse iteratively.

Nim
# 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)
Coding Round
90. Detect cycle in linked list

Use Floyd’s cycle detection (tortoise and hare).

Nim
# 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()
Coding Round
91. Two sum problem

Find two numbers that add to target.

Nim
# 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")
Coding Round
92. Maximum subarray sum (Kadane)

Kadane’s algorithm for maximum contiguous subarray sum.

Nim
# 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)
Coding Round
93. Longest common subsequence

DP solution for LCS.

Nim
# 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.
Coding Round
94. Knapsack problem

0/1 knapsack DP.

Nim
# 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)
Coding Round
95. N‑Queens

Place N queens on an N×N board.

Nim
# 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)
Coding Round
96. Sudoku solver

Backtracking solver for 9x9 Sudoku.

Nim
# 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)
Coding Round
97. Merge two sorted arrays

Merge into a new sorted sequence.

Nim
# 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 item
Coding Round
98. Find missing number

Given array 0..n with one missing, find it using XOR or sum.

Nim
# 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)
Coding Round
99. Check balanced parentheses

Use a stack.

Nim
# 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 user
Coding Round
100. Implement a stack

Use a sequence as underlying storage.

Nim
# 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.name

Frequently Asked Questions

For common doubts about Nim, refer to the official documentation or community resources.