Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Late Binding

A call whose function the compiler can see (a name bound to a lambda, a function argument it can trace to one) is resolved when the program compiles. A call through a function value it cannot trace, one read out of a data structure for example, is late bound: the runtime figures out which function it calls, and at compile time only the function’s type is known. Late binding is a powerful abstraction tool. For example we can create two structs of type T that each contain a different implementation of f, and we can use them interchangibly with any function that accepts a T. In this simple example we create one implementation of f that increments the count, and one that decrements it.

type T = { count: i64, f: fn(t: T) -> T };
let ts: Array<T> = [
  { count: 0, f: |t: T| {t with count: t.count + 1} },
  { count: 0, f: |t: T| {t with count: t.count - 1} }
];
let t = array::iter(ts);
(t.f)(t)

when run this example will output,

{count: 1, f: Abstract(|t: T| { t with count: t.count + 1 })}
{count: -1, f: Abstract(|t: T| { t with count: t.count - 1 })}

You can see that f is bound to a different function in each struct, and the call ran each one’s body. While Graphix is not an object oriented language, you can use closures and late binding to achieve some of the same outcomes as OOP.