After many many hours of trying to make this work, I finally got it to go. I
suspect I'm not the only person to struggle with this, so I figured I'd
document the answer here.
Rather than getting into theoreticals, let's just jump into code.
Nim supports inheritance for objects. An example:
# Parent object
type
Animal = ref object of RootObj
id: int
method sayFeet(a: Animal): string =
result = "[" & $a.id & "] has an unknown number of feet"
# Child object
type
Dog = ref object of Animal
hairLength: float
legCount: int
method sayFeet(d: Dog): string =
result = "Dog [" & $d.id & "] has " & $d.legCount & " feet."
# Child object
type
Cat = ref object of Animal
whiskerCount: int
pawCount: int
method sayFeet(c: Cat): string =
result = "Cat [" & $c.id & "] has " & $c.pawCount & " feet."
# generic proc
proc describe(foo: Animal) =
echo "animal detail: " & sayFeet(foo)
# use it
var
sparky = Dog(id: 1, legCount: 4)
mittens = Cat(id: 2, pawCount: 4)
sparky.describe() # "animal detail: Dog [1] has 4 feet."
mittens.describe() # "animal detail: Cat [2] has 4 feet."
Run
Yes, this and all the examples are silly. This more about proof-of-concept.
Nim also supports generics in object types. Among other things, this allows for
compile-time optimization.
type
Domestication = enum
Wild
Feral
Domestic
type
Animal[wildness: static[Domestication]] = ref object of RootObj
id: int
proc sayFeet(a: Animal): string =
result = "[" & $a.id & "] has an unknown number of feet"
proc describe(foo: Animal) =
when foo.wildness == Wild:
echo "Wild animal detail: " & sayFeet(foo)
elif foo.wildness == Feral:
echo "Feral animal detail: " & sayFeet(foo)
else:
echo "Tame animal detail: " & sayFeet(foo)
var
sparky = Animal[Feral](id: 1)
mittens = Animal[Domestic](id: 2)
sparky.describe() # "Feral animal detail: [1] has an unknown number of
feet."
mittens.describe() # "Tame animal detail: [2] has an unknonw number of
feet."
Run
But, can you put both ideas together? Yes, but there are apparently a few
things to keep in mind.
The example of both:
# Parent object
type
Domestication = enum
Wild
Feral
Domestic
type
Animal[wildness: static[Domestication]] = ref object of RootObj
id: int
proc sayFeet(a: Animal): string =
result = "[" & $a.id & "] has an unknown number of feet"
# Child object
type
Dog[wildness: static[Domestication]] = ref object of Animal[wildness]
hairLength: float
legCount: int
proc sayFeet(d: Dog): string =
result = "Dog [" & $d.id & "] has " & $d.legCount & " feet."
# Child object
type
Cat[wildness: static[Domestication]] = ref object of Animal[wildness]
whiskerCount: int
pawCount: int
proc sayFeet(c: Cat): string =
result = "Cat [" & $c.id & "] has " & $c.pawCount & " feet."
# generic proc
proc describe(foo: Animal) =
when foo.wildness == Wild:
echo "Wild animal detail: " & sayFeet(foo)
elif foo.wildness == Feral:
echo "Feral animal detail: " & sayFeet(foo)
else:
echo "Tame animal detail: " & sayFeet(foo)
# use it
var
sparky = Dog[Feral](id: 1, legCount: 4)
mittens = Cat[Domestic](id: 2, pawCount: 4)
sparky.describe() # "Feral animal detail: Dog [1] has 4 feet."
mittens.describe() # "Tame animal detail: Cat [2] has 4 feet."
Run
Some things to watch out for:
* For inheritance in general, you really need "ref object" rather than
"object" objects. Things get lost otherwise.
* Notice the ref object of Animal[wildness] on the child object definitions.
The parent object reference must include the generics.
* Inheritance WITHOUT generics: use "method" not "proc". WITH generics, use
"proc" not "method". And no, I don't know why.
Hopefully this is helpful to somebody. :-)