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.