home

Interfaces In Nim

May 14, 2016

One of the things you're going to run into when you first use nim, is that it lacks interfaces. As far as I'm concerned, that's a big limitation. However, there is a pattern available to us, which, as far as I can tell, is the "nim way."

The trick is to use a tuple of procs. Let's look at an example. First, we'll create our "interface":

type
  IFileSource = tuple[
    read: proc(path: string): string
  ]

The above creates a new type, IFileSource which is a tuple containing a single key, read. read references a procedure which takes a path to a file (as a string) and returns the file's contents (also as a string).

From the execution point of view, this behaves just like an interface. We can create a procedure which takes an IFileSource and call its read method:

proc process(fs: IFileSource) =
  let config = fs.read("config")
  ...

Hopefully, every thing is clear. Next, let's create an implementation which reads from the local file system:

from os import nil

type
  LocalFileSource = object
    root: string

proc read(fs: LocalFileSource, file: string): string =
  return readFile(os.joinPath(root, file))

All that's left is to associate our implementation with our interface. The little mental barrier you need to tear down is that this is an explicit / manual process. By that, I mean we need to create a procedure that converts one to the other:

proc toFileSource(fs: LocalFileSource): IFileSource =
  return (
    read: proc(file: string): string = fs.read(file)
  )

To use it, we'd do:

let fs = LocalFileSource(root: "./data/")
process(fs.toFileSource())

For simple interfaces like this one, we can [arguably] simplify the code by replacing our LocalFileSource with a closure:

proc localFileStore(root: string): IFileSource =
  return (
    read: proc(file: string): string =
      readFile(os.joinPath(root, file))
  )

process(localFileStore("./data/"))

I'll confess that it took me a few minutes of staring at the example I found before understanding it. But it's quickly become second nature. Having said that, I've seen a couple comments suggesting that Go-like interfaces (easily one of Go's best features) could be added in the future. That would be nice.