On my free time I have been tinkering with Nim, and have taken a liking to the idea of building my personal website (almost) entirely with Nim. There are some js libs I frequently use, so I took this opportunity to try and create some Nim bindings. Mostly, the experience has been pretty smooth and awesome! I do have some questions though (I am far from a Nim expert, mind you).
Sometimes, js libs will have functions defined on the prototype of an object, and these will behave differently depending on the arguments passed. Say for example:
global.Vector.prototype.add = function(x, y, z) {
if (x instanceof global.Vector) {
this.x -= x.x || 0;
this.y -= x.y || 0;
this.z -= x.z || 0;
return this;
}
if (x instanceof Array) {
this.x -= x[0] || 0;
this.y -= x[1] || 0;
this.z -= x[2] || 0;
return this;
}
this.x -= x || 0;
this.y -= y || 0;
this.z -= z || 0;
return this;
}
As far as I can tell, such a function must be a property of the type.
type
VectorObj{.importc.} = object
add*: proc(...)
But it can't be redefined there and varargs and OR-ing (ie. proc(x: float | Vector | openArray[float], y, z: float = 0) are not concrete.
Is there some established practice on how to wrap such a function?
Overloads should help.
type
Vector* = ref VectorObj
VectorObj{.importc.} = object
proc add*(v1, v2: Vector) {.importcpp.}
proc add*(v1: Vector, v2: array[3, float]) {.importcpp.}
proc add*(v1: Vector, x, y, z: float) {.importcpp.}
# ... etc
That's a pragma I hadn't seen. Thank you! It is amazing how convenient you've managed to make ffi-ing with Nim.
@slimshady Not yet, but I will put it on github eventually and I'll be sure to ping you as soon as I do! It will take some though, maybe you'll be done before that :P
Here we go again.
How do we usually go about binding "static" js function calls? I'm not sure if that's the correct term for Nim, but maybe it gives the idea. A case would be, if we follow the same Vector example, that the add function has a "static" overload that returns a new Vector instead of modifying the caller.
[...]
global.Vector.add = function(v1, v2) {
//implementation
return result;
}
[...]
let sumVec = global.Vector.add(v1, v2);
How is this usually approached?
proc add(t: typedesc[Vector], a, b: Vector): Vector {.importcpp: "global.Vector.add(@)".} # typedesc[vector] is needed here only for disambiguation with previous add function.
let v = Vector.add(a, b)
However I would not call that idiomatic =).
Nice, thanks!
That's actually great food for thought. I've been thinking that, since it's a binding, it makes sense that it is used just as in Javascript (Vector.add()), but another argument might be that whoever uses the nim bindings probably is attracted by the nim way of doing things, so something more idiomatic might be better. Maybe a good compromise is to have both available, the Javascripty Vector.add() and a more nim-style addVectors(). The fact that both are workable solutions is a testament to the flexibility of this wonderful language :D.