Object-Oriented Programming in Nim
17.1 Inheritance
Inheritance requires of RootObj (or another inheritable object) or the {.inheritable.} pragma.
Mark leaf types {.final.} to prevent further inheritance.
type
Animal = ref object of RootObj
name: string
Dog = ref object of Animal
breed: string
Cat = ref object of Animal17.2 Dynamic Dispatch with method
proc = static dispatch (resolved at compile time). method = dynamic dispatch (vtable).
Nim 2 compiles methods using vtables (--multimethods:off by default; multi-methods are opt-in).
method speak(a: Animal): string {.base.} = "..."
method speak(d: Dog): string = d.name & " says Woof"
method speak(c: Cat): string = c.name & " says Meow"
let pets: seq[Animal] = @[Dog(name: "Rex"), Cat(name: "Tom")]
for p in pets: echo p.speak()
# Calling the parent implementation
method speak(d: Dog): string = procCall(speak(Animal(d))) & " / Woof"17.3 Runtime Type Checks and Conversion
for p in pets:
if p of Dog: echo "Dog breed: ", Dog(p).breed # checked conversion (ObjectConversionDefect if wrong)17.4 Composition over Inheritance
Prefer composition, object variants, and generics/concepts. Nim isn't class-centric. Procedures live outside types, and UFCS makes them feel like methods.
17.5 Encapsulation
Module-level visibility with *. Getters and setters:
type Account* = object
balance: float # private
proc balance*(a: Account): float = a.balance
proc `balance=`*(a: var Account, v: float) =
if v < 0: raise newException(ValueError, "negative")
a.balance = v17.6 Interfaces Without Inheritance
Tuples of closures, concepts (Module 30), or vtable-like objects of procs.