I'm trying to understand how generics are instantiated; I figured they would let me separate the generic declaration with a specified implementation in a different module, but it seems they don't work that way in practice. I'm trying to build an MCTS library that doesn't rely on specific details of the data structures and algorithms being used, for example:
Consider a.nim, which has a generic dostuff method that relies on an algorithm specified elsewhere:
# a.nim
proc impl*[T](x: T): string =
quit "bug: override me"
proc dostuff*[T](x: T) =
echo "Calling: ", impl(x)
The actual implementation of impl is in main.nim:
# main.nim
import a
type Foo = object
proc impl*(x: Foo): string =
return "object: Foo()"
dostuff(Foo())
This doesn't work, I expected "Calling: object: Foo()" but the override resolution instead uses the generic definition.
Functions like $ work this way seamlessly. I can have one module define a proc `$`*(x: Foo) and another module that doesn't require it can call $ on the object, so I don't understand why this works
try this:
proc dostuff*[T](x: T) =
mixin impl
echo "Calling: ", impl(x)
mixin forces the symbol impl to be open
otherwise the only symbol that dostuff can possibly use is the one defined before dostuff
the default for symbols is mixin if two or more declarations are available and closed if only one is available
if no symbols are available then it's an ident and that also works
if on the other hand you wanted to ensure that no other symbols can be used after declaration you could instead do
proc dostuff*[T](x: T) =
bind impl
echo "Calling: ", impl(x)
look at https://nim-lang.org/docs/manual.html#generics-symbol-lookup-in-generics for more information
It only works (ie. impl is an open symbol) if there are 2 definition available in a.nim which makes it an overloaded symbol and is therefore open. Not sure if this is the correct behavior, but this is how it works. For it to work, add
proc impl(x: int): string = $x