Interfaces In Nim
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.