brownplt / lambda-py

Other
58 stars 10 forks source link

Assignment to self in constructors affects object being constructed #47

Closed jpolitz closed 11 years ago

jpolitz commented 11 years ago

Test that shows the bug in https://github.com/brownplt/lambda-py/blob/scope-rewrite/tests/bugs/assign-to-self.py

Observed behavior: Inside init, assignment to self changes the object being constructed.

Expected behavior: Inside init, self is just like any other variable that happens to be bound to the object being constructed. If you assign to it, locally the self variable changes, but no objects are magically moved around.

class O(object):
  def __init__(self):
    self = ['why am i a list now']

o = O()
assert(type(o) != list)
jpolitz commented 11 years ago

Worse, the list and tuple constructors in builtins/ rely on this behavior.

amtriathlon commented 11 years ago

The interpreter part is easy to fix: just make init a normal method call, but since the library has to be modified to avoid the reliance on this behavior I think it could be the moment to implement the real constructor new and let init be what it really is, an initializer, in fact all the class calling logic could be moved from the interpreter to the type.call method, that's the way I've implemented it in my own project.

Something like this

class type(object):
    def __call__(cls, *args):
        obj = cls.__new__(cls, *args)
        if type(obj) is cls:
            obj.__init__(*args)

class object has default new static method and init method, like this:

class object:
    def __new__(cls, *args):
        return ____delta("CObject", cls, None)
    def __init__(self, *args):
        pass

the rest of builtin classes implement new to create builtin objects. init is rarely needed for them.

Since you are already working in the builtins if you take that part I could do the interpreter and type parts.

2013/2/26 Joe Politz notifications@github.com

Worse, the list and tuple constructors in builtins/ rely on this behavior.

— Reply to this email directly or view it on GitHubhttps://github.com/brownplt/lambda-py/issues/47#issuecomment-14134745 .

Alejandro.

jpolitz commented 11 years ago

Sounds great! You'll probably need to define a new macro for CObject (you can just call it object), but delta is specifically for CBuiltinPrims.. This is especially nice because it moves special-cased logic out of the interpreter and into core code, so that's awesome.

I'm working my way through changes to list() to make it similar to what I've done to tuple() at the moment; I'm going to keep pressing on through the builtins.

Note that I chose to use a special %-identifier to get tuples to work (%tuple). I'm happy to change this later, but I did the simple thing for expediency's sake, so I'm going to define %list, %dict, %set, as well.

On Tue, Feb 26, 2013 at 5:52 PM, Alejandro Martinez < notifications@github.com> wrote:

The interpreter part is easy to fix: just make init a normal method call, but since the library has to be modified to avoid the reliance on this behavior I think it could be the moment to implement the real constructor new and let init be what it really is, an initializer, in fact all the class calling logic could be moved from the interpreter to the type.call method, that's the way I've implemented it in my own project.

Something like this

class type(object): def call(cls, _args): obj = cls.new(cls, *args) if type(obj) is cls: obj.init(_args)

class object has default new static method and init method, like this:

class object: def new(cls, args): return ____delta("CObject", cls, None) def init(self, args): pass

the rest of builtin classes implement new to create builtin objects. init is rarely needed for them.

Since you are already working in the builtins if you take that part I could do the interpreter and type parts.

2013/2/26 Joe Politz notifications@github.com

Worse, the list and tuple constructors in builtins/ rely on this behavior.

— Reply to this email directly or view it on GitHub< https://github.com/brownplt/lambda-py/issues/47#issuecomment-14134745> .

Alejandro.

— Reply to this email directly or view it on GitHubhttps://github.com/brownplt/lambda-py/issues/47#issuecomment-14145197 .

amtriathlon commented 11 years ago

The problem is not limited to init: any method can play havoc assigning to self:

class O(object):
    def m(self):
        self = ['why am i a list now']

o = O()
o.m()
assert(type(o) != list)

Here the self aliasing mechanism is causing this, but the background problem is mutator methods in the library depends on this behavior when using immutable representations, for example list.append, this is a birth defect in base code, the new regime just makes it crystal clear...

amtriathlon commented 11 years ago

I've commited most of the changes needed to remove the class call special case, it needs more testing before the removal, but I've done a partial commit since it doesn't broke anything and so the code can be reviewed.

amtriathlon commented 11 years ago

This was fixed in the new regime for addresses and VPointer's https://github.com/brownplt/lambda-py/commit/eebf668cabf57bf9dad47732055d5fd07d58346c