As I described in the earlier posts Nim didn’t support “super calls” when using methods instead of statically bound procs and generics. My article caused a little bit of discussion around this on IRC and Andreas decided to implement the mechanism he already had planned - but had not fully decided a good name for.
The other day this mechanism entered the devel branch which means it will be official in the next release of Nim, which I suspect will be out before the end of 2014. It should be noted that devel is mainly undergoing bug fixing, so unless you are paranoid it’s pretty usable. Now… of course I had to try out super calls in my sample code…
Instead of calling it static_call
- implying that the call resolution is made at compile time statically, the name ended up as procCall
- implying the call resolution is simply done just like its done for procs. Same, same - different words. To put it another way, even though we are calling a method, let the static types of the arguments decide which method to call, not the actual runtime types.
A bit of repetition from the earlier articles - today you can select among overloaded procs to call by using type conversion, so if you want to call myProc
that takes an argument of type A
when you have an object of type B
in your hand (B
being a sub type of A
), you just do myProc(A(b))
.
This is called a type conversion and can be viewed as a type safe cast, it only works if its safe to do it. Nim also has cast
but generally its something you should only use if you know what the heck you are doing. :)
Now… methods don’t rely on static typing - they resolve based on the actual runtime type of the objects - that’s their whole reason for existing and this is essential in supporting more complex OO code. So the type conversion technique in itself only works for selecting among overloaded procs, not methods.
But now, with the addition of procCall
we can call overloaded methods using the exact same technique. Our Fruit code from earlier articles can now be simplified - we no longer need to factor out the base implementation of calcPrice
as a method under a different name basePrice
. And it still works as intended, here is the updated code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
|
As we can see the “syntactic style” can vary as usual, in line 40 we can use procCall without parenthesis, but on line 51 we need to use it in a “calling style” in order for precedence to work out.
So… one may wonder, why not just have something like super
as in Smalltalk or Java etc? The reason is quite simple, Nim supports multiple dispatch - in other words in Nim we can dispatch based on the types of several of the arguments, as long as they are objects. There is no specific argument in the method call that is privileged as “self”, we just tend to use the first argument by convention as “the receiver”. This also means that “super” has no reasonable meaning in Nim, super of who?
The type conversions you would use together with procCall
does spell out the “super type” explicitly (in our example above - “Fruit”), thus making it very clear which method you want to call. A puritan would possibly claim that it “couples” the class with its superclass too much (compared to the more abstract uncoupled “super”). But I think this explicit style fits the Nim mindset better - and even if this makes changing superclasses a bit more tedious (you need to hunt down procCalls and fix the type conversions) - its nothing that some future refactoring tools couldn’t easily fix, and changing superclasses isn’t what you do every minute anyway.
One could also argue that the intention of the programmer is slightly lost with this style. It doesn’t obviously read as “call the super implementation”, and I suspect we may find coders using this also throwing in a small comment stating that hey, this is a super call.
The mechanism also enables “super super” since you can explicitly skip over intermediate classes which of course is generally bad style, but at the same time, explicitness follows the principle of least surprise :)
Calling overloads on self
Another scenario we easily could have is a class with several overloads of a method foo
in which one or many of them wants to call the “primary” implementation of the behavior. This is in fact technically the same scenario as the “super call”, and would be solved the same way - its just an example of multiple dispatch where we are dispatching on more than the first argument. Let’s say we have a Foo class which holds a seq of fruit names, for some … odd reason. And we want to be able to add fruit names to it by sending in various different objects, well, a Fruit obviously, but also collection of Fruits etc.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
|
As we can see above we need to use procCall
on line 15 and 19 in order to be able to call the add
method that takes a Fruit from the other add
methods. So yes, this is not only useful for doing classic super calls.
Conclusion
The mechanism procCall
solves the super call problem with methods, and it also solves similar use cases along the way. As far as I can tell this removes the last “hurdle” for being able to do serious OO coding in Nim.
Go Nim!