There have been a number of threads on the mailing list asking for ability to overwrite RapydScript operators, to which I have objected due to performance and clarity of generated JavaScript. However, I do understand the use case of developers wanting this functionality. Likewise, there are multiple cases where I could find operator overloading useful myself - one of the simplest cases is == test between arrays, which is a notorious problem with JavaScript (and for which RapydScript currently relies on deep_eq).
I've recently been considering adding new operators (even though I'm against adding unpythonic features) to handle these cases. My idea was using the '.' prefix for the operators to signify that they can be overloaded (the names would match their Python equivalent overload names (
https://docs.python.org/2/library/operator.html), or perhaps even use the same name as a method name: i.e. 'Array.== = ' which would compile to 'Array.__eq__').
There are a couple things that appeal to me about the '.' prefix:
- It's unobtrusive and compact, yet alerts the developer that the operator isn't a standard JS operator
- '.' is already used to signify a method call of a class/object, these overloadable operators would compile into method calls, hence this seems like a consistent convention
The idea is that each '.' operator would compile into something like this:
original:
a .== b
compiled:
_$rapyd$__eq__(a, b)
function _$rapyd$__eq__(a, b) {
if (typeof a.__eq__ === 'function') return a.__eq__(b); // user-defined
return deep_eq(a, b); // default case, a slower but saner equality test
}
The equivalent method would get generated for all operators used (similar to other baselib logic). This, of course, would still require you to tweak your Python code slightly when porting it to RapydScript, but it wouldn't be a tedious task for the few cases that need it. I'm thinking of allowing these alternatives for all operators:
.=, .==, .!=, .<, .>, .+, ./, .*, .-, .and, .or, as well as binary operators
Additionally, I was thinking of introducing a 'debug' mode for compilation, that would, using logic similar to the logic above compile all operators into something safer yet slower, allowing the developer to catch errors earlier. For example:
original:
string + integer
debug compile:
_$rapyd__debugadd__(string, integer)
function _$rapyd$__debugadd__(a, b) {
if (typeof a === typeof b) {
if (typeof a === 'object') throw new TypeError("Concatenation of objects/arrays using + operator will result in implicit conversion to string or NaN, use .+ operator instead");
return a + b;
} else {
throw new TypeError(''Cannot add/concatenate "' + typeof a + '" and "' + typeof b + '" objects');
}
}
As you can see above, debug mode wouldn't rely on the same functions as the '.' prefix, but the structure would be similar. Whereas '.' would check if overloaded method exists, and if not, call the safer/pythonic function, debug would not check overloaded method at all, but would warn the user when the likely intent is to use the safer/pythonic function. The debug mode would run slower but give developer much better sanity checks when the code is executed.
- addition of '.' prefix for each operator to signify overloaded/pythonic version of it without forcing the overhead of pythonic version on all cases
- addition of debug mode that would rely on similar logic to temporarily overload basic operators during a debug build for stricter type checking
I wanted to hear the community's yay or nay for each of the above ideas, as well as any gotchas with syntax/logic you think would arise that I may have overlooked.