Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

problem with tuple+obj and list+obj expressions #888

Open
jggatc opened this issue Dec 30, 2024 · 2 comments
Open

problem with tuple+obj and list+obj expressions #888

jggatc opened this issue Dec 30, 2024 · 2 comments

Comments

@jggatc
Copy link

jggatc commented Dec 30, 2024

There is an apparent problem with tuple + obj and list + obj expressions. Rather than call obj.__radd__, it seems that tuple.__add__/list.__add__ are called. Similar problem occurs with tuple * obj and list * obj. Problem appears to stem from the fact that tuple and list have __add__ and __mul__ methods that seem to take priority.

python code:

# __pragma__ ('opov')

class Obj:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self._message = None
    def __str__(self):
        return 'obj'
    def __add__(self, other):
        self._message = 'obj.__add__'
        return (self.x + other[0], self.y + other[1])
    def __sub__(self, other):
        self._message = 'obj.__sub__'
        return (self.x - other[0], self.y - other[1])
    def __radd__(self, other):
        self._message = 'obj.__radd__'
        return (other[0] + self.x, other[1] + self.y)
    def __rsub__(self, other):
        self._message = 'obj.__rsub__'
        return (other[0] - self.x, other[1] - self.y)
    @property
    def message(self):
        message = self._message
        self._message = None
        return message

obj = Obj(4, 2)
_tuple = (4, 2)
_list = [4, 2]

print('obj + tuple:', obj + _tuple, '\t\tcalled:', obj.message)
print('obj - tuple:', obj - _tuple, '\t\tcalled:', obj.message)
print('tuple + obj:', _tuple + obj, '\tcalled:', obj.message)
print('tuple - obj:', _tuple - obj, '\t\tcalled:', obj.message)
print('obj + list:', obj + _list, '\t\tcalled:', obj.message)
print('obj - list:', obj - _list, '\t\tcalled:', obj.message)
print('list + obj:', _list + obj, '\tcalled:', obj.message)
print('list - obj:', _list - obj, '\t\tcalled:', obj.message)

console output:

obj + tuple: (8, 4) 		called: obj.__add__
obj - tuple: (0, 0) 		called: obj.__sub__
tuple + obj: [4, 2, obj] 	called: None
tuple - obj: (0, 0) 		called: obj.__rsub__
obj + list: (8, 4) 		called: obj.__add__
obj - list: (0, 0) 		called: obj.__sub__
list + obj: [4, 2, obj] 	called: None
list - obj: (0, 0) 		called: obj.__rsub__

env: Python3.9 venv with pip install Transcrypt
testing: Firefox version 133 in Linux

@jggatc
Copy link
Author

jggatc commented Dec 31, 2024

This is a solution to deal with the issue with tuple + obj, tuple * obj, list + obj, and list * obj expressions. As tuple and list constructs are built on Javascript Array, perhaps this issue is due to maintaining the efficiency of tuple/list __add__ and __mul__ methods, therefore developers may prefer to maintain efficiency of these tuple/list functionality. The following changes to Transcrypt Array __add__ and __mul__ methods can be added to your personal code to fix the issue:

__pragma__ ('js', {},
"""
Array.prototype.__add__ = function (aList) {
    if (typeof aList == 'object' && '__radd__' in aList) {
        return aList.__radd__ (this);
    }
    else {
    return list (this.concat (aList));
    }
};
""")
__pragma__ ('js', {},
"""
Array.prototype.__mul__ = function (scalar) {
    if (typeof scalar == 'object' && '__rmul__' in scalar) {
        return scalar.__rmul__ (this);
    }
    else {
    let result = this;
    for (let i = 1; i < scalar; i++) {
        result = result.concat (this);
    }
    return result;
    }
};

@jggatc
Copy link
Author

jggatc commented Jan 2, 2025

Although the change in the previous comment solves the issue it may not be the best approach. The Python way would be to have type checking and return NotImplemented if incorrect type that would trigger __radd__ \ __rmul__. Transcrypt does not seem to have NotImplemented. With Transcrypt I think the following approach is better, and it solves the tuple + obj / list + obj / tuple * obj / list * obj issue while maintaining tuple + tuple / list + list / tuple * number / list * number functionality. The pragmas can be added to personal code to fix the issue.

__pragma__ ('js', {},
"""
Array.prototype.__add__ = function (other) {
    if (other.__class__ == this.__class__) {
        return list (this.concat (other));
    }
    else if (typeof other == 'object' && '__radd__' in other) {
        return other.__radd__ (this);
    }
    else {
        throw py_TypeError ('unsupported operand type');
    }
};
""")

__pragma__ ('js', {},
"""
Array.prototype.__mul__ = function (other) {
    if (typeof other == 'number') {
        let result = this;
        for (let i = 1; i < other; i++) {
            result = result.concat (this);
        }
        return result;
    }
    else if (typeof other == 'object' && '__rmul__' in other) {
        return other.__rmul__ (this);
    }
    else {
        throw py_TypeError ('unsupported operand type');
    }
};
""")

Tested Javascript changes on local Transcrypt cloned from current git repository. Tested with compiled Python code similar to first comment, verified that tuple and list functionality (+, +=, *, *=) was maintained, and that tuple + obj / list + obj called obj.__radd__ and tuple * obj / list * obj called obj.__rmul__. In addition, the automated_tests showed no errors. I can make a pull request of revised Javascript code if this change is acceptable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant