Friday, March 01, 2013

Correction: Javascript NOT more dynamic than Python

EDITED: Adam showed me that I had missed a way to do something similar in Python as I added below.

After a very long hiatus, I'm finally back and working on a web version of rur-ple, my "Karel the Robot in Python" program.  Since it's going to be on the web, I have decided to have both a Javascript version and a Python version (using Brython).  As I was exploring javascript prototype-based "inheritance" pattern, I found  what I think is a neat way to illustrate how dynamic the method lookup can be in Javascript, in a way that has (to my knowledge) no parallel in Python.

As in rur-ple, I have a robot "class" called UsedRobot which is broken (i.e. like the original Karel it can only turn left, etc.).  For completely separate reasons, I had decided that I would actually implement a PrivateRobot "class" within the RUR  namespace, which would not be directly exposed to the user, and have UsedRobot "inherit" from it.

Here's what one can can do

var r = new UsedRobot();      // does not have a turn() method
RUR.PrivateRobot.prototype.turn = RUR.PrivateRobot.prototype.turn_left;   // give such a method to the "parent"
r.turn();  // turns left using the new parent's method.
UsedRobot.prototype.turn = RUR.PrivateRobot.prototype.__turn_right;  //give different method to "child"
r.turn();  //  turns right - look up stops at "child"
delete UsedRobot.prototype.turn;
r.turn(); // turns left again  - look up goes up to parent since method no longer exists in child.

I still prefer Python to Javascript ... but I find that one can do neat things in Javascript which (to my knowledge) can not be done in Python.   Caveat: the fact that something like the above can be done in Javascript does not mean that it should be done.

Adam, in the comments, pointed out that something similar can be done with Python, using instance.__class__ to modify the behaviour of a class.  For example:

class A:

class B(A):
    def turn(self):
        print("turn left")

def right(self):
    print("turn right")

b1 = B()
b2 = B()
a1 = A()
b1.turn()  # prints turn left
a1.__class__.turn = right
a1.turn() # prints turn right
b1.turn() # prints turn left
del b1.__class__.turn
b1.turn()  # prints turn right