RapydScript: __call__ special method not working
Context
class tester:
def __init__(self):
print("init")
def go(self):
print("go")
# specal method:
def __call__(self, arg):
print("call:", arg)
test = tester()
test.go()
test.__call__(1)
test(2) # <<<< invoke the special method; use object as functor
Expected and Desired Behavior (ordinary python)
init
go
call: 1
call: 2
This works just fine in python2.7, python3.4, ipython2.7, and ipthon3.
Observed Behavior (RS)
init
go
call: 1
[stdin]:152
test(2);
^
TypeError: object is not a function
at [stdin]:152:1
at Object.exports.runInThisContext (vm.js:74:17)
at Object.<anonymous> ([stdin]-wrapper:6:22)
at Module._compile (module.js:460:26)
at evalScript (node.js:431:25)
at Socket.<anonymous> (node.js:164:11)
at Socket.emit (events.js:129:20)
at _stream_readable.js:908:16
at process._tickCallback (node.js:355:11)
Workaround
Just invoke test.__call__(...) instead of test(...)
Discussion
There are ways of making functors in javascript, using closures … but I reckon it is both easier (at compile time) and more efficient (at run time) if the compiler just catches the pythonic functor expression and maps it to the __call__
special method.
About this issue
- Original URL
- State: closed
- Created 9 years ago
- Comments: 20 (11 by maintainers)
To summarize, the problems with using function objects to implement
__call__
are:Changing object prototype dynamically is not available in all JS runtimes. This could potentially be worked around by manually copying properties, but IMO, that is just a slow, ugly mess.
The list of properties and type of the object will be changed by simply adding a
__call__
method to the object. This is highly unexpected. See code below:No evidence beyond vague hand-waving was presented to counter the claim on MDN that dynamically changing the prototype will slow down attribute access on the resulting object
I am willing to ignore this –
__call__
simply wont work for runtimes without this facility.This is a problem. In particular the type() of the resulting object changing is highly unexpected. Any code based on type inspection that expects an object will break.
This is a problem, unless someone can present some actual evidence that it is not.
IMO the call operator
()
suffers from the same problem as all other operators in RS – namely that it cannot be overloaded. Any solution for this problem should be a general solution for the operator overloading problem as a whole. I am still inclined towards simply enabling all operator overloading on a per-module basis. This allows the user to choose which is more important to him – performance or python compatibility, on a fairly granular level.The issue with that is the different behavior of indexOf() when used with non-primitive types. I am inclining towards ignoring that as the cost of doing business. The RS provided index() method can be made to work (it will check if the passed in argument has an
__eq__
method, and if it does it will loop through the array checking with__eq__
and otherwise fallback to indexOf). This should cover 99% of the use cases. It is still not fully compatible with python since in theory, a custom class could has an__eq__
method that returns true for primitive types, but that should be pretty rare.There is a similar issue with the python-like dict and set types. Currently in my fork I have an implementation for them that use the JS Map and Set types where available. The problem with that is that they do not use
__hash__
and__eq__
. This can be worked around by implementing a hashmap in pure JS that will use those methods when defined, otherwise falling back to toString().