I guess I'll use a converter.
I still hope that we get an answer of a core dev. That use case is very common in OO languages. So I am asking myself already for a long time how to handle it, maybe I have already asked others, I can not really remember. I have for example a lot of legacy Ruby code that extends standard types and C lib types.
You say that the table type is final. So I wonder why is that necessary. Well, myTable = newTable() construct is a problem indeed.
Your question is related to my one from some weeks ago
https://forum.nim-lang.org/t/2982
As most users coming from other higher level OO languages like Ruby would expect it just to work, I decided to provide it for GTK3. Beside something like "myButton = newButton()" we have an initButton(myExtendedButton). See my post above. Seems to work fine.
Converters -- well Araq does not like converters too much, and many converters can make compile slow.
Stefan: I still hope that we get an answer of a core dev. That use case is very common in OO languages. So I am asking myself already for a long time how to handle it, maybe I have already asked others, I can not really remember. I have for example a lot of legacy Ruby code that extends standard types and C lib types.
While the ambivalent attitude of Nim towards object-oriented programming is still one of my major issues with the language, inheritance is not really the solution for this particular problem. You'd get the fragile base class problem and you'd get covariance issues. What you really want is something like automated delegation (where you can specify that certain calls are forwarded to a component), a feature that unfortunately is rarely supported for statically typed languages. You can do it manually, of course, but that becomes a bit of a headache with the amount of functionality supported for tables.
Sufficiently powerful metaprogramming can in principle also offer a solution.
you can actually overload the . operator (and maybe the .= and .() ones as well) to do this. You can have a macro that goes through the children and selects the one that matches. A cool side effect of doing things this way is that the memory layout of the object is much more obvious and you can control which things come from where. A well maintained nimble library for doing this would be nice, as it's easy to generate somewhat hard to understand errors, and edge cases. There's subfield but it applies to EVERYTHING and thus causes unwanted side-effects (and can blow out the compiler by going down type after type).
to be honest this:
type base1 = object
f1: int
f2: float
type base2 = object
f3: string
type child = object {.child: thing1, thing2.}
thing1: base1
thing2: base2
seems quite nice to me. And it decouples use of OO for virtual dispatch from it's use for composition (remember inheritance IS composition with some funky rules!). It will also make construction order very obvious oh I'll post an example
type PkgInstall* = object
pkg*: Pkg
version*: PkgVer
layout*: PkgLayout
macro `.`*(pkg: PkgInstall, field: string): untyped =
## ^ This makes PkgInstall types act like contatinations
## of the Pkg, PkgVer and PkgLayout types
var subfields = pkg.getType.last
expectKind(subfields, nnkRecList)
for elm in subfields:
var fields = elm.getType.last
expectKind(fields, nnkRecList)
var real_field = findChild(fields, eqIdent(it, $field))
if real_field != nil:
result = newDotExpr(pkg, elm).newDotExpr real_field
return
template `.=`*(pkg: PkgInstall, field: string, rval: untyped) =
`.`(pkg, field) = rval
Obviously needs {.experimental.} and it's really only for that one type as of now.