Methods

Procedures always use static dispatch. Methods use dynamic dispatch. For dynamic dispatch to work on an object it should be a reference type.

  1. type
  2. Expression = ref object of RootObj ## abstract base class for an expression
  3. Literal = ref object of Expression
  4. x: int
  5. PlusExpr = ref object of Expression
  6. a, b: Expression
  7. method eval(e: Expression): int {.base.} =
  8. # override this base method
  9. raise newException(CatchableError, "Method without implementation override")
  10. method eval(e: Literal): int = return e.x
  11. method eval(e: PlusExpr): int =
  12. # watch out: relies on dynamic binding
  13. result = eval(e.a) + eval(e.b)
  14. proc newLit(x: int): Literal =
  15. new(result)
  16. result.x = x
  17. proc newPlus(a, b: Expression): PlusExpr =
  18. new(result)
  19. result.a = a
  20. result.b = b
  21. echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))

In the example the constructors newLit and newPlus are procs because they should use static binding, but eval is a method because it requires dynamic binding.

As can be seen in the example, base methods have to be annotated with the base pragma. The base pragma also acts as a reminder for the programmer that a base method m is used as the foundation to determine all the effects that a call to m might cause.

Note: Compile-time execution is not (yet) supported for methods.

Note: Starting from Nim 0.20, generic methods are deprecated.

Multi-methods

Note: Starting from Nim 0.20, to use multi-methods one must explicitly pass --multimethods:on when compiling.

In a multi-method, all parameters that have an object type are used for the dispatching:

  1. type
  2. Thing = ref object of RootObj
  3. Unit = ref object of Thing
  4. x: int
  5. method collide(a, b: Thing) {.base, inline.} =
  6. quit "to override!"
  7. method collide(a: Thing, b: Unit) {.inline.} =
  8. echo "1"
  9. method collide(a: Unit, b: Thing) {.inline.} =
  10. echo "2"
  11. var a, b: Unit
  12. new a
  13. new b
  14. collide(a, b) # output: 2

Inhibit dynamic method resolution via procCall

Dynamic method resolution can be inhibited via the builtin system.procCall. This is somewhat comparable to the super keyword that traditional OOP languages offer.

  1. type
  2. Thing = ref object of RootObj
  3. Unit = ref object of Thing
  4. x: int
  5. method m(a: Thing) {.base.} =
  6. echo "base"
  7. method m(a: Unit) =
  8. # Call the base method:
  9. procCall m(Thing(a))
  10. echo "1"