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.

Reply via email to