Allow aliasing object methods without passing an object

Propose new features and changes
eugenesv
Posts: 175
Joined: 21 Dec 2015, 10:11

Allow aliasing object methods without passing an object

21 Sep 2023, 04:26

A working intuitive example in Javascript, where you alias a class method and then just call it

Code: Select all

class myClass {
  static getKey(key) {
    return `your key is ${key}`;
  }
}
const myGetKeyAlias = myClass.getKey;
console.log(myGetKeyAlias ("mykey"))
AutoHotkey requires passing an explicit this

Code: Select all

class myClass {
  static getKey(key) {
    return 'your key is ' key
  }
}
myGetKeyAlias    := myClass.getKey ; fails since no explicit this is passed
myGetKeyAlias_Bind    := myClass.getKey.Bind(myClass)
There is another roundabout way to get the alias, but besides its indirectness, it also doesn't catch typos untill you call the alias

Code: Select all

myGetKeyAlias_ObjBind    := ObjBindMethod(myClass, 'getKey')

(this is prompted by viewtopic.php?p=540029#p540029)
iseahound
Posts: 1448
Joined: 13 Aug 2016, 21:04
Contact:

Re: Allow aliasing object methods without passing an object

21 Sep 2023, 12:09

As far as I know, this is generally done because of the use of callbacks such as DllCall and other windows functions. The implicit this is overwritten by the first argument specified by an external caller.

In other words, how should CallbackCreate(myClass.getKey) work?

Another useful case would be a class full of mixins.
eugenesv
Posts: 175
Joined: 21 Dec 2015, 10:11

Re: Allow aliasing object methods without passing an object

21 Sep 2023, 15:02

Not sure since I don't understand the issue deeply enough, why can't CallbackCreate(myClass.getKey) work by passing the parameters without overwriting this?

And can those DllCalls continue to work as they currently do without breaking intuition in the basic case?
lexikos
Posts: 9593
Joined: 30 Sep 2013, 04:07
Contact:

Re: Allow aliasing object methods without passing an object

21 Sep 2023, 20:06

It has nothing to do with callbacks or DllCall.

There is no "overwriting this". this is just a parameter of the function, and doesn't have a value until you provide one when you call or bind the function. When you call myClass.getKey(key), myClass is passed as the first parameter. If you call myGetKeyAlias := myClass.getKey, myGetKeyAlias(key), key is passed as the first parameter and the second parameter has no value.

A static method is just a function attached to a class object. I chose not to give it any special behaviour, instead making it work the same as a method of any object, for flexibility and simplicity. One aspect of this that you make use of frequently (probably without realizing it) is that this is the class object which you used to call the method, not necessarily the class object which defines the method. For instance, myClass inherits Call from Object, but Object.Call creates an object derived from this.Prototype, so myClass() (equivalent to myClass.Call()) constructs an instance of myClass instead of an instance of Object. You can override static Call for your own class and subclasses.

this is not a parameter in JavaScript. When you call myGetKeyAlias, this is undefined. If you call myClass.getKey, this is myClass. To correct this, you need to use myClass.getKey.bind(this), the same as in AutoHotkey.

If you want to be able to do method := myClass.getKey, method(key), you need to make the getKey property return a function object which already has myClass bound to it. Someone using method := mySubclass.getKey may want it to instead bind to mySubclass instead of myClass.

Other objects should behave the same, so for instance, for each new Map, myMap.Get should return a new function object bound to myMap. I believe Python takes this approach; in Python, Map.Get would return an unbound function which requires this/self to be specified, and myMap.Get would return a bound method. But in Python, assigning a function to an attribute (property) of an instance does not create a method (i.e. this is not passed automatically).

If you create a method with x.y := z, x.y will return z. Would you want it to return z.Bind(x) instead? That wouldn't make sense. If it doesn't do that, it would be inconsistent with other methods.

If myMap.Get returns a bound method but you aren't going to call it, it is wasting CPU time and memory. For instance, maybe you just want to check the properties of the function, such as MinParams/MaxParams. You also can't apply the method to some other object, as with DefProp := {}.DefineProp, DefProp(obj, name, {...}).
eugenesv
Posts: 175
Joined: 21 Dec 2015, 10:11

Re: Allow aliasing object methods without passing an object

22 Sep 2023, 04:40

Python also doesn't require any self passing for class methods, and static methods explicitly don't even have self.

Code: Select all

class myClass:
  @staticmethod
  def getKey_Static    (     key):
    return f'@static  : your key is {key}'
  def getKey_Instance  (self,key):
    return f'@Instance: your key is {key} and self is {type(self)}'

myClassI                    	= myClass()
myGetKeyAlias_static        	= myClass .getKey_Static
myGetKeyAlias_Instance      	= myClassI.getKey_Instance
print(myGetKeyAlias_static  	("mykey")) # self is undefined
print(myGetKeyAlias_Instance	("mykey")) # class __main__.myClass
lexikos wrote:
21 Sep 2023, 20:06
A static method is just a function attached to a class object.
lexikos wrote:
21 Sep 2023, 20:06
I chose not to give it any special behaviour, instead making it work the same as a method of any object, for flexibility and simplicity.
Unfortunately requiring accounting for a hidden this is not simple, I think the Python example is even better than JS (guess it part of "explicit better than explicit" Py rule)
lexikos wrote:
21 Sep 2023, 20:06
To correct this, you need to use myClass.getKey.bind(this), the same as in AutoHotkey.
Ok, but my point is that I don't need to correct this
lexikos wrote:
21 Sep 2023, 20:06
Someone using method := mySubclass.getKey may want it to instead bind to mySubclass instead of myClass.
So let them use the extra Bind :)? Why can't the default be that this is myClass unless overriden?
lexikos wrote:
21 Sep 2023, 20:06
If myMap.Get returns a bound method but you aren't going to call it, it is wasting CPU time and memory.
The solution to issues with dead code is eliminating dead code (with some tooling help)

lexikos wrote:
21 Sep 2023, 20:06
If you create a method with x.y := z, x.y will return z. Would you want it to return z.Bind(x) instead? That wouldn't make sense.
So in Python in this case you still don't need any self

Code: Select all

def ret1nkey(key):
  return f'1 and {key}'
myClass.x = ret1nkey
myClassAlias_x = myClass.x
print(myClassAlias_x("mykey"))
lexikos
Posts: 9593
Joined: 30 Sep 2013, 04:07
Contact:

Re: Allow aliasing object methods without passing an object

22 Sep 2023, 20:56

eugenesv wrote:
22 Sep 2023, 04:40
Unfortunately requiring accounting for a hidden this is not simple,
It is very simple. Whether you can personally remember to account for it is a different matter.
The solution to issues with dead code is eliminating dead code
You must have misunderstood my point. Read the example I gave. Dead code has nothing to do with it.
Why can't the default be that this is myClass unless overriden?
I already gave at least one answer for this.

How or when would it default or be overridden? If it binds the parameter automatically, how would it determine when the caller is trying to override it?

If you believe it can be done without breaking scripts, show us. You can change the behaviour of the property corresponding to a method by using DefineProp. You can do this for all methods via static __New and OwnProps, although if you only apply it to your own classes, it won't prove much.
[Python] static methods explicitly don't even have self
So static methods in Python don't automatically bind self, because they don't have self. AutoHotkey static methods have this, as I already pointed out. Scripts rely on it. Scripts also rely on myClass.getKey or similar returning the function object with nothing bound to this.
iseahound
Posts: 1448
Joined: 13 Aug 2016, 21:04
Contact:

Re: Allow aliasing object methods without passing an object

22 Sep 2023, 22:13

I wonder if you're getting @staticmethod confused with @classmethod...

AutoHotkey's static which uses this acts more like @classmethod... which requires an explicit cls as the first parameter.
eugenesv
Posts: 175
Joined: 21 Dec 2015, 10:11

Re: Allow aliasing object methods without passing an object

23 Sep 2023, 09:12

iseahound wrote:
22 Sep 2023, 22:13
AutoHotkey's static which uses this acts more like @classmethod... which requires an explicit cls as the first parameter.
Ok, but if it requires class as a parameter, it's not static and shouldn't be called that
eugenesv
Posts: 175
Joined: 21 Dec 2015, 10:11

Re: Allow aliasing object methods without passing an object

23 Sep 2023, 11:36

lexikos wrote:
22 Sep 2023, 20:56
It is very simple. Whether you can personally remember to account for it is a different matter.
The issue with this design is not the quality of my memory, but that mandating keeping the invisible handling there adds complexity, and the fact that it's unexpected/uncommon (vs JS/Py) doesn't help
lexikos wrote:
22 Sep 2023, 20:56
If it binds the parameter automatically, how would it determine when the caller is trying to override it?
when the caller binds it manually with a methods that allows overriding the automatic behavior
lexikos wrote:
22 Sep 2023, 20:56
If you believe it can be done without breaking scripts, show us.
I believe that the design should be improved because the current one is poor, not that this wouldn't break anything
lexikos wrote:
22 Sep 2023, 20:56
although if you only apply it to your own classes, it won't prove much.
Yep
lexikos wrote:
22 Sep 2023, 20:56
So static methods in Python don't automatically bind self, because they don't have self.
That's the good part! But then even class/instance methods don't require an explicit cls/self
lexikos wrote:
22 Sep 2023, 20:56
AutoHotkey static methods have this, as I already pointed out.
That doesn't make it less of an issue
lexikos wrote:
22 Sep 2023, 20:56
Scripts rely on it
Sure, and other scripts rely on a more intuitive and common convention of what static is and when this/self is needed to be passed explicitly in non-static class methods, but they have to do this weird double myClass...Bind(myClass)
lexikos
Posts: 9593
Joined: 30 Sep 2013, 04:07
Contact:

Re: Allow aliasing object methods without passing an object

23 Sep 2023, 22:44

You are complaining about imagined complexity, and not seeing the complexity that your request would add, if it was even possible to implement while retaining compatibility with scripts.

As I said, if you believe it can be done without breaking scripts, show us. I give up trying to explain anything to you.
eugenesv
Posts: 175
Joined: 21 Dec 2015, 10:11

Re: Allow aliasing object methods without passing an object

24 Sep 2023, 01:09

I complain about issues in the most common use cases, the ones I've encountered when writing scripts, so it's not imaginary despite you insistence

It's ok if these common cases are made simpler and consistent at the expense of more complexity in more niche use cases

And again, I make no claim about backward compatibility, only that this complexity trade-off is better design
ntepa
Posts: 429
Joined: 19 Oct 2022, 20:52

Re: Allow aliasing object methods without passing an object

24 Sep 2023, 04:19

lexikos wrote:How or when would it default or be overridden?
Would something like this work?

Code: Select all

class myClass {
    static getKey(key) {
        return 'your key is ' key
    }
}

class anotherClass {
    static getKey(key) {
        return (myClass.getKey)(this, key)
    }
}

Func.Prototype.DefineProp("Call", {Call:CallMethod})
CallMethod(Func, Params*) {
    func_is_method := InStr(Func.Name, ".")
    first_param_is_object := Params.Has(1) && Params[1] is Object
    if func_is_method {
        if (first_param_is_object) && Params.Length >= Func.MinParams {
            return Func(Params*)
        } else {
            arr := StrSplit(Func.Name, ".")
            method := arr.Pop()
            obj := ""
            for part in arr
                obj := obj ? obj.%part% : %part%
            return Func(obj, Params*)
        }
    }
    return Func(Params*)
}

myGetKeyAlias := myClass.getKey
myGetKeyAlias2 := anotherClass.getKey

OutputDebug myGetKeyAlias.Call("a")
OutputDebug myGetKeyAlias2.Call("b")
lexikos
Posts: 9593
Joined: 30 Sep 2013, 04:07
Contact:

Re: Allow aliasing object methods without passing an object

24 Sep 2023, 22:29

@ntepa No, and that's my point. If (myClass.getKey) defaults the first parameter to myClass, how would it know whether the caller is trying to override this or just supply the second parameter? (In this case the method only takes one other parameter, but in other cases it could be variadic or have optional parameters that the caller may or may not be specifying.)

@eugenesv If the complexity isn't imaginary, explain what it is.
And again, I make no claim about backward compatibility
I don't recall that being stated before. If it is not backward compatible, it can't be implemented regardless of who thinks which design is better. Not that I agree with your opinion.
bonobo
Posts: 75
Joined: 03 Sep 2023, 20:13

Re: Allow aliasing object methods without passing an object

25 Sep 2023, 09:36

this is not a parameter in JavaScript. When you call myGetKeyAlias, this is undefined. If you call myClass.getKey, this is myClass. To correct this, you need to use myClass.getKey.bind(this), the same as in AutoHotkey.
Yep, relevant ...
https://exploringjs.com/impatient-js/ch_objects.html#extracting-methods

Return to “Wish List”

Who is online

Users browsing this forum: No registered users and 50 guests