News for hackers. Stuff that matters.
Re:Crafting Interpreters: Superclasses by pansa2 on Wednesday March 18, 2020 @01:05PM (#22618829)
As stated in the text, for `super` calls to work within a method, it’s necessary to know the class on which the method is defined. Here, that’s easy - it’s the class that the method is lexically defined within.

This is more difficult in a language which allows methods to be added to classes dynamically. IIRC both Python and JavaScript are in this category, but still calculate `super` lexically. Therefore, if a method is copied from one class to another, `super` within that method may refer to the wrong class.

Re:Crafting Interpreters: Superclasses by blattimwind on Wednesday March 18, 2020 @01:18PM (#22618951)
> Therefore, if a method is copied from one class to another, `super` within that method may refer to the wrong class.

No, super() checks that "self" is an instance of the given class.

So if you take a method defined in a class hierarchy "A -> B", and attach it to an instance of "C" (i.e. c.foo = B.foo) and call it like "c.foo(c)", which you have to do, since you didn't invoke the descriptor protocol to bind the method, you will get a type error, because c is not an instance of A/B. That is also true if you attach the method to the class (C.foo = B.foo); you can call it like a method, but super() still checks the type and will complain.

Also, if you take a bound method and attach it to another object (i.e. c.foo = b.foo), it is already bound and you can't pass a "self" different from the object it's bound to. E.g. if you are passing around a callback that's a method.

Note how Class.method is just a function, while instance.method is a bound method. That's due to the descriptor protocol of functions (__get__/__set__).

Re:Crafting Interpreters: Superclasses by pansa2 on Wednesday March 18, 2020 @01:44PM (#22619262)
> That is also true if you attach the method to the class (C.foo = B.foo); you can call it like a method, but super() still checks the type and will complain.

You're right. Here's some example Python code:

    >>> class A:
    ...     def foo(self):
    ...             print('A.foo')
    >>> class B(A):
    ...     def foo(self):
    ...             super().foo()
    ...             print('B.foo')
    >>> b = B()
    >>> b.foo()
    A.foo
    B.foo

    >>> class C:
    ...     def foo(self):
    ...             print('C.foo')
    >>> class D(C):
    ...     def foo(self):
    ...             super().foo()
    ...             print('D.foo')
    >>> d = D()
    >>> d.foo()
    C.foo
    D.foo
Now, if I do `D.foo = B.foo` and call `d.foo()`, I do indeed get a "TypeError: super(type, obj): obj must be an instance or subtype of type". That's better than getting `A.foo B.foo`, as happens in JavaScript.

However, if `super` were truly dynamic, I would have expected to get `C.foo B.foo`.

Re:Crafting Interpreters: Superclasses by blattimwind on Wednesday March 18, 2020 @04:36PM (#22620971)
IIRC super() is actually syntactic sugar for super(ClassTheDefIsFoundIn, nameOfFirstArgument), because there is no way I can think of to implement that as a function at runtime.
Re:Crafting Interpreters: Superclasses by pansa2 on Wednesday March 18, 2020 @07:51PM (#22622761)
Yes, I think that’s right. However, I think it would work better if classTheDefIsFoundIn were resolved dynamically rather than lexically. Perhaps it would have been possible to do this by modifying the descriptor protocol?
Re:Crafting Interpreters: Superclasses by blattimwind on Thursday March 19, 2020 @09:46AM (#22627421)
I'm not sure that's possible, because you could only work with type(self), and that breaks MRO traversal. (A chain of super().x() calls in a hierarchy like A->B->C would always call the first parent class, because it has no way to tell >where< in the hierarchy it is. That's the reason why the class argument is required.)

I think that would need an entirely differently designed mechanism and can't be bolted on.

Re:Crafting Interpreters: Superclasses by wool_gather on Wednesday March 18, 2020 @01:10PM (#22618858)
Interesting point. I'd have to look at my implementation to be sure, but Lox will also have this quirk, won't it? You can freely assign values, (as I recall) including functions, to a property on an instance.

Or does it only come up when a method is added to the class?

Re:Crafting Interpreters: Superclasses by munificent on Wednesday March 18, 2020 @01:44PM (#22619257)
Lox makes a distinction between functions and methods, so you won't run into this problem. This is unlike JavaScript, but like most other object-oriented languages.

If you take a function (which may be a reference to a method) and store it in a field on some instance, the function remembers its original bindings to "this" and "super", which is generally what you want and expect.

Lox doesn't allow you to mutate a class by adding new methods to it.

Re:Crafting Interpreters: Superclasses by pansa2 on Wednesday March 18, 2020 @01:18PM (#22618954)
> Lox will also have this quirk, won't it?

Possibly - I’m not sure about the details of method lookup in Lox. Does `this` even work correctly when a method is assigned to an instance?

Re:Crafting Interpreters: Superclasses by munificent on Wednesday March 18, 2020 @01:45PM (#22619270)
If you store a function or reference to a method in a field on an instance in Lox, the function will remember its original `this` binding. That's what Python, C#, Ruby, etc. do and is generally what you want. It's not what JS does because JS doesn't really know what a "method" is, or at least didn't before classes were added.
Re:Crafting Interpreters: Superclasses by pansa2 on Wednesday March 18, 2020 @01:52PM (#22619357)
Is it possible that there isn't a `this` binding? Does Lox allow access to "unbound" methods directly from a class, or are methods accessible only via instances?
Re:Crafting Interpreters: Superclasses by munificent on Wednesday March 18, 2020 @02:07PM (#22619536)
Methods are only accessible from instances, so `this` is always meaningfully bound. If you try to use `this` outside of a class body, it's a static error.
Re:Crafting Interpreters: Superclasses by pansa2 on Wednesday March 18, 2020 @02:23PM (#22619691)
OK, that solves the problem. The issue with unbound methods in Python is that they don’t yet have a value for `this`, but they do have a fixed value for `super`.
Re:Crafting Interpreters: Superclasses by philips4350 on Wednesday March 18, 2020 @09:52PM (#22623590)
IIRC in JavaScript every method of a class has a [[HomeObject]] property in it, referencing the class it was defined in to solve this problem