Hi,
I have this little problem and I don't understand why this does not compile:
proc create* [T] (): T =
new(result)
init(result)
type
TFoo = ref object
x: Int
proc init(self: TFoo) =
self.x = 11
var foo = create[TFoo]()
echo foo.repr()
the compiler rejects this code with undeclared identifier: init
for me init is declared at the time when create is instantiated with TFoo,
Now I extent the example to make the compiler happy with some init():
proc init(x: int) = nil
proc create* [T] (): T =
new(result)
init(result)
type
TFoo = ref object
x: Int
proc init(self: TFoo) =
self.x = 11
var foo = create[TFoo]() # line 15
echo foo.repr()
now it complains instantiated from line 15 - type mismatch got TFoo , expected init(x: int).
This behavior of the compiler makes generics less powerful (and I believe this worked differently before)
First code breaks forward declaration rule, which is why you get "undeclared identifier: init". Moving create declaration to after init() makes the compiler happy.
Second code is actually the same, but with different case. At the time create() is created, the only known init() is the one with x: int parameter, so the init with self: TFoo is unknown, which is why when create() got instantiated with TFoo, it can't select init with TFoo parameter. Again, reordering the declaration solves the problem.
thanks, but I don't see that the first example breaks forward declaration rule, because at the time when create is instantiated (line 15), init(self:TFoo) is declared already. The compiler used to work this way (at least I have some code which does the same as the first example and it did compile with an older version) and I think it should work, because it makes generics more powerful.
Now I have define all the types and methods which could use a generic proc before. How can I write a generic library, which doesn't know all the types which will use it in advance ? - I have to use class hirachies and virtual methods, which I wanted to avoid !
IMHO this is the contrary to generic
Yes it used to work differently in 0.8.14. However since even the stdlib had some embarrassing typos which even the tests did not find, I decided the old way is too error prone. So now undeclared identifiers are an error in generics. However, this does not make generics in any way less powerful: There are "open" and "closed" symbols. Per default any overloaded symbol is "open" which means that the instantiation context is considered in an instantiation. This means that your init example would work if it'd been overloaded:
proc init(x: int) = nil
proc init(x: float) = nil
proc create* [T] (): T =
new(result)
init(result)
This is however an ugly workaround: The next version will support a mixin declaration within a generic so that you can explicitely tell it's an "open" symbol:
proc create* [T] (): T =
mixin init
new(result)
init(result)
Oh and these new symbol lookup rules may become the default in templates too for consistency.
BTW I'm not that pleased with the new immediate templates: Most templates are in fact immediate so it looks like we picked the wrong default. Fortunately the planned changes for overloading resolution will fix this as a side effect. ;-)