Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Class definition syntax for AutoHotkey


  • Please log in to reply
96 replies to this topic
fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009

While we're at this topic, were there technical reasons why you decided to use the base mechanism instead of creating a (shallow?) copy? Or was it a design decision?

The premise of your question seems to be that such a decision was consciously made. I never seriously considered cloning as an option (and I'm slightly curious about why anyone would). Keep in mind that the base mechanism was designed back in 2009, and the class definition syntax was intended purely as syntax sugar around the existing design. That said, actual run-time inheritence seems much more logical to me than "inheritence" by cloning, and is also more flexible.

I think it might cause slight confusion if some parts of the object (i.e. some base or sub-base) are shared with other objects. When you modify a key that is stored in the base object and it gets set in the object itsself like a4u said above, then the structure of the object gets compromised and it can not be known where a key may be found when enumerating? Please correct me if I'm understanding something wrong here. I'm also not sure how this is treated when there is an object stored under the key and variables are added/removed from the object. Is a copy of the object stored in the instance of the class, or is it still shared in all instances? I'll try to write an example of this later if my argumentation seems unclear.

Edit: I think it might help to avoid this at least somewhat if the member variables were declared when an instance of the obejct is created, as opposed to declaring them in the class object itself. This behavior is used by C# (see here: <!-- m -->http://msdn.microsof...y/ms173118.aspx<!-- m --> ).
My point is mostly that the "new" keyword carries the meaning of creating a new instance, without shared components.

flak
  • Members
  • 283 posts
  • Last active: Jan 01 2012 06:20 PM
  • Joined: 02 Oct 2009

I'm also not sure how this is treated when there is an object stored under the key and variables are added/removed from the object.

Something like this?
class test1
{
  var a:={1:2}
}

test2:=new test1
test3:=new test1
msgbox % test3.a.1
test2.a.1:=3
msgbox % test3.a.1



guest3456
  • Members
  • 1704 posts
  • Last active: Nov 19 2015 11:58 AM
  • Joined: 10 Mar 2011
contrary to what i thought, there is actually a good amount of reading material out there on prototype based oop. it seems the original language was called Self

anyway, heres a link if anyone wants to do some reading. i dont have time to go through all this now, but ill probably pick at it

<!-- m -->http://stackoverflow... ... d-language<!-- m -->

fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009

I'm also not sure how this is treated when there is an object stored under the key and variables are added/removed from the object.

Something like this?
class test1
{
  var a:={1:2}
}

test2:=new test1
test3:=new test1
msgbox % test3.a.1
test2.a.1:=3
msgbox % test3.a.1


Yes. I will try it when I get home.

Edit: I tried it, and as I thought, it won't get duplicated. This effectively makes it impossible to declare arrays this way and forces you to declare them in the constructor. It's not much of a problem, but I found this behavior to be very irritating. Languages like C# use static keyword for members that are shared between all instances of a class. Other members get created when the instance is created to avoid this issue. It's basically a kind of pre-constructor.

a4u
  • Guests
  • Last active:
  • Joined: --

... forces you to declare them in the constructor.

Yes - it's essentially the difference between a Class Variable & an Instance Variable. I could produce this same "problem" in Ruby.

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

When you modify a key that is stored in the base object

The only way to modify a value stored in the base object is explicitly, with obj.base.foo := bar or some other direct reference to the base object. There's no reason to do it if you don't want to modify the value for every object derived from obj.base.

... it can not be known where a key may be found when enumerating?

For-loops only return the key-value pairs contained by the object itself, not inherited values.

I'm also not sure how this is treated when there is an object stored under the key and variables are added/removed from the object. Is a copy of the object stored in the instance of the class, or is it still shared in all instances?

I'm not sure what you mean. If obj.base.foo := Object(), objects are no different to any other value in that context. Only the reference to the object is copied into obj.base. That reference may be inherited at run-time (i.e. by obj.foo), but is never automatically copied into obj.

I think it might help to avoid this at least somewhat if the member variables were declared when an instance of the obejct is created,

I think you mean declare them in the class definition (the only place it can be done), but have them exist directly in the object. Aside from fields containing objects, there generally isn't any point.

My point is mostly that the "new" keyword carries the meaning of creating a new instance, without shared components.

Every instance of the class shares code, static members, and the meta-data which describes the contents of the object.

This effectively makes it impossible to declare arrays this way and forces you to declare them in the constructor.

I see. That is a bit of a problem. However, my main reason for adding "var" in v1.1 was specifically for static data, which otherwise requires dirty hacks like this:
class C {
    _Init() {
        global C
        static _ := C._Init()
        this.var := value
    }
}


fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009

This effectively makes it impossible to declare arrays this way and forces you to declare them in the constructor.

I see. That is a bit of a problem. However, my main reason for adding "var" in v1.1 was specifically for static data, which otherwise requires dirty hacks like this:
class C {
    _Init() {
        global C
        static _ := C._Init()
        this.var := value
    }
}

That's my point mostly. If you declare something as var in a class, I wouldn't expect it to be a static variable that is shared in all instances. This is counterintuitive if you come from languages like Java or C#.
I would propose renaming the "var" keyword to "static" or "static var" and make the "var" keyword create variables/objects in the instance object instead of the shared base. This would avoid the issue with arrays/objects I mentioned above and make it possible to list variables in the class definition that are different in each instance. In my opinion instance variables are used much more than static variables, so it should be possible to declare them as member variables in the class definition.

guest3456
  • Members
  • 1704 posts
  • Last active: Nov 19 2015 11:58 AM
  • Joined: 10 Mar 2011

That's my point mostly. If you declare something as var in a class, I wouldn't expect it to be a static variable that is shared in all instances. This is counterintuitive if you come from languages like Java or C#.


thats because you 'expect' that there actually is a 'class', where in ahk there is no such thing. the 'class' is just a wrapper for an actual instance of an object, there is no 'class' blueprint. everything is an instance. in ahk, when you type your class definition, you are actually creating an instance of an object with the properties you define. its what i was figuring out when i made this post previously in the thread

its counterintuitive because you are using your class-based oop thinking in a langauge that isn't class-based, its prototype-based. i'm trying to remove those limitations from my head as well. the only "problem" that i see, if there is one, is that the syntax sugar using the "class" and "new" keywords makes the user more likely to assume that ahk is a class-based lang. and thats why a4u suggested changing the "class" keyword to "prototype" in this post.


I would propose renaming the "var" keyword to "static" or "static var" and make the "var" keyword create variables/objects in the instance object instead of the shared base. This would avoid the issue with arrays/objects I mentioned above and make it possible to list variables in the class definition that are different in each instance. In my opinion instance variables are used much more than static variables, so it should be possible to declare them as member variables in the class definition.


while i'm not opposed to this, should care be taken not to mix and match a class-based object structure with a prototype-based object structure? is there any merit to just picking one and going with it?

maul.esel
  • Members
  • 790 posts
  • Last active: Jan 05 2013 09:26 PM
  • Joined: 28 Feb 2011
[*:6zthmfoq]a suggestion / feature request: something like IsClass() would be helpful IMO.
[*:6zthmfoq]a question: how can I use RegisterCallback() with a class function?Regards
maul.esel
Join the discussion on The future of AutoHotkey
Posted Image Visit me on github Posted Image
Win7 HP SP1 64bit | AHK_L U 64bit

a4u
  • Guests
  • Last active:
  • Joined: --

... IsClass() would be helpful IMO.

You could always check if the object has a __Class key:
IsClass(obj) {
	return, obj.haskey("__Class")
}
Also, I was curious if there is any though of creating properties that return a calculated value - other than using the __Get metafunction.

flak
  • Members
  • 283 posts
  • Last active: Jan 01 2012 06:20 PM
  • Joined: 02 Oct 2009

a question: how can I use RegisterCallback() with a class function?

RegisterCallback("classname.functionname")


maul.esel
  • Members
  • 790 posts
  • Last active: Jan 05 2013 09:26 PM
  • Joined: 28 Feb 2011
Thx you two. (I tried RegisterCallback(class.function) which of course didn't work :oops:)

Edit: I guess there's no way to register an instance-specific function (thus, making this contain a specific instance when the function is called)
Edit 2: I found a workaround that should work: just give RegisterCallback() a pointer to this as EventInfo param:
RegisterCallback("function", "", 3, Object(this))
Join the discussion on The future of AutoHotkey
Posted Image Visit me on github Posted Image
Win7 HP SP1 64bit | AHK_L U 64bit

fragman
  • Members
  • 1591 posts
  • Last active: Nov 12 2012 08:51 PM
  • Joined: 13 Oct 2009

That's my point mostly. If you declare something as var in a class, I wouldn't expect it to be a static variable that is shared in all instances. This is counterintuitive if you come from languages like Java or C#.


thats because you 'expect' that there actually is a 'class', where in ahk there is no such thing. the 'class' is just a wrapper for an actual instance of an object, there is no 'class' blueprint. everything is an instance. in ahk, when you type your class definition, you are actually creating an instance of an object with the properties you define. its what i was figuring out when i made this post previously in the thread

its counterintuitive because you are using your class-based oop thinking in a langauge that isn't class-based, its prototype-based. i'm trying to remove those limitations from my head as well. the only "problem" that i see, if there is one, is that the syntax sugar using the "class" and "new" keywords makes the user more likely to assume that ahk is a class-based lang. and thats why a4u suggested changing the "class" keyword to "prototype" in this post.


I would propose renaming the "var" keyword to "static" or "static var" and make the "var" keyword create variables/objects in the instance object instead of the shared base. This would avoid the issue with arrays/objects I mentioned above and make it possible to list variables in the class definition that are different in each instance. In my opinion instance variables are used much more than static variables, so it should be possible to declare them as member variables in the class definition.


while i'm not opposed to this, should care be taken not to mix and match a class-based object structure with a prototype-based object structure? is there any merit to just picking one and going with it?

Of course I'm biased by other languages, but I'm still convinced that non static variables are easier to grasp, both for programmers coming from other languages and for newbies. I think that especially the new keyword strongly suggests that a new instance of the class is created without shared components between other instances.
I believe you're already mixing both principles by implementing class syntax. Using prototypes as base is perfectly fine, but i think that class variables work better when being non-static by default because this behavior is probably more used.

IsNull
  • Moderators
  • 990 posts
  • Last active: May 15 2014 11:56 AM
  • Joined: 10 May 2007

Edit: I guess there's no way to register an instance-specific function (thus, making this contain a specific instance when the function is called)

maul.esel: Func() [AHK 1.1] returns a delegate Object pointing to a specific instance & Member Method ;)

Func(FunctionName) [v1.1.00+]: If FunctionName does not exist explicitly in the script (by means such as #Include or a non-dynamic call to a library function), Func() returns 0. Otherwise, it returns a reference to the function. This can be used to call the function or retrieve information such as the minimum and maximum number of parameters.


Edit: Sorry, it seems that it wont work :roll:

Here an Up-To-Date Version of my Delegate Class:
class Delegate
{
   var Reference := null
   var MethodName := ""
   
	Invoke(args*) {
		if(IsObject(this.Reference)){
			invokationTarget := this.Reference
			return invokationTarget[this.MethodName](args*)
		} else if(IsFunc(this.MethodName)) {
			dynfunc := this.MethodName
			return dynfunc.(args*)
		} else
			msgbox, 16, error, Can't find Invocation Target
	}
   
	__New(args*){      
		i := 0
		for each, a in args
			i++
		if(i == 1){
			this.MethodName := args[1]
		} else {
			this.Reference := args[1]
			this.MethodName := args[2]
		}
	}
}


Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
Func() returns a reference to a function. Methods are just functions which have a hidden "this" parameter. If you try to use one as a callback, "this" will simply contain the first parameter of the callback - an integer. Furthermore, Func("Class.Method") relies on the undocumented fact that each class method is added to the script's list of functions. This is to support secondary functionality such as #Warn LocalSameAsGlobal, and is not essential to the functionality of the class. To retrieve a reference to any method-function, you can simply use Class.Method (after declaring global Class if appropriate). Even so, it has no connection to the object which you used to retrieve the reference.