Tying a procedure to a type is typically done by adding an overload. In the
binary tree example, you would want to define a function such as
[cmp](https://nim-lang.org/docs/system.html#cmp%2CT%2CT) or the `<` operator
for the Key type used to instantiate the tree. In the binary tree module
itself, you would typically use the
[mixin](https://nim-lang.org/docs/manual.html#generics-mixin-statement) keyword
to indicate the functions that should be defined in the scope of the caller.
Sometimes, we need stateful comparison function (for example, we might be
sorting coordinates by their distance to a particular point). Then the binary
tree may hold an abstract "Comparison State" value that can be passed as an
additional parameter to the `cmp` function.
type
BinaryTree[T, CmpState = void] = object
rootNode: ref BinaryTreeNode[T]
when CmpState isnot void:
cmpState: CmpState
BinaryTreeNode[T] = object
value: T
left, right: ref BinaryTreeNode[T]
proc insert[T, CmpState](tree: var BinaryTree[T, CmpState], val: T) =
mixin cmp # We allow the user to overload `cmp` for the value type
var curNode = tree.rootNode
while curNode != nil:
# Here, we make sure to call `cmp` with the right parameters
let cmpRes = when CmpState is void: cmp(val, curNode.value)
else: cmp(tree.cmpState, val, curNode.value)
# The search continues according to the result above
if cmpRes == 0:
...
elif cmpRes > 0:
...
else:
...
Run
The above solution is probably the most idiomatic at the moment (before
[concepts](https://nim-lang.org/docs/manual_experimental.html#concepts) become
stable). Now, if you really want to have the associated proc sitting as a
generic parameter, you can either go for `static` and file issues for the
encountered problems, or you can use techniques similar to [tag
dispatching](https://www.fluentcpp.com/2018/04/27/tag-dispatching/) in C++. The
BinaryTree type can feature an extra type parameter that will be used for
overload selection (typically with templates) to fetch constants, procs and
other items associated with the type:
import strutils
type
ComparisonStyle1 = object
ComparisonStyle2 = object
template cmpFunction(x: type ComparisonStyle1): untyped =
cmp
template cmpFunction(x: type ComparisonStyle2): untyped =
cmpIgnoreStyle
echo cmpFunction(ComparisonStyle2)("foo_bar", "fooBar")
Run
* * *
> I also believe that generic parameters should be static by default:
The reason why the generic parameters are not static by default is historic.
When you specify a type for a generic parameter, it acts as a constraint. The
generic will be instantiatable only with types matching the constraint and you
would usually use a [type
class](https://nim-lang.org/docs/manual.html#generics-type-classes) to specify
it.