Added Method Functionality

Propose new features and changes
User avatar
Delta Pythagorean
Posts: 627
Joined: 13 Feb 2017, 13:44
Location: Somewhere in the US
Contact:

Added Method Functionality

18 May 2020, 23:27

I have two ideas:

1.)
In a class there's all kinds of things you can do with a method, but sometimes you want to have private and public functions for people to use.
For example, I have a method that displays a message, and another that adds an index for how many times the method's been called:

Code: Select all

Loop, 4
	Messages.Alert("I've been called at least: ")
Return

Class Messages {
	Alert(Text) {
		MsgBox, 0x0, Alert!, % Text . This.__Times__()
	}

	; We only want to allow this function to be called inside the class, not outside!
	__Times__() {
		Static I := 0
		I += 1
		Return, (I)
	}
}
Some languages have it where you can specify which is public and which is private before the method name.
Some others have it with a built in variable that's an array of all of the method's names.
It would be nice to have a way to only allow certain methods to be called inside the class instead of letting the user call any method they want and possibly cause problems.

2.)
Documentation of a method inside a class is very useful at times. Python does this by having three double quotes just under the function definition:

Code: Select all

def my_function():
	"""I just display True so..."""
	return True
Doing something for AHK shouldn't be too hard right...? I'd say have some sort of meta function that defines the documentation of the methods.

Just a thought :)

[AHK]......: v2.0.12 | 64-bit
[OS].......: Windows 11 | 23H2 (OS Build: 22621.3296)
[GITHUB]...: github.com/DelPyth
[PAYPAL]...: paypal.me/DelPyth
[DISCORD]..: tophatcat

User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: Added Method Functionality

19 May 2020, 03:30

The second suggestion should probably be in its own topic.
Recommends AHK Studio
User avatar
Delta Pythagorean
Posts: 627
Joined: 13 Feb 2017, 13:44
Location: Somewhere in the US
Contact:

Re: Added Method Functionality

20 May 2020, 06:46

Fine by me, I'll split them as soon as I get back from work.

[AHK]......: v2.0.12 | 64-bit
[OS].......: Windows 11 | 23H2 (OS Build: 22621.3296)
[GITHUB]...: github.com/DelPyth
[PAYPAL]...: paypal.me/DelPyth
[DISCORD]..: tophatcat

HotKeyIt
Posts: 2364
Joined: 29 Sep 2013, 18:35
Contact:

Re: Added Method Functionality

20 May 2020, 06:51

In AHK v2 you can do:

Code: Select all

msg:=Messages.new()
Loop 4
  msg.Alert("I've been called at least: ")
Return

Class Messages {
  Alert(Text) {
    MsgBox Text . times()
    
    times() {
        Static I := 0
        I += 1
        Return I
    }
  }
}
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Added Method Functionality

20 May 2020, 07:07

u can, but its not quite the same as a private method
HotKeyIt
Posts: 2364
Joined: 29 Sep 2013, 18:35
Contact:

Re: Added Method Functionality

20 May 2020, 07:39

Yes in that case it is a private function inside method.
guest3456
Posts: 3454
Joined: 09 Oct 2013, 10:31

Re: Added Method Functionality

20 May 2020, 10:59

why not just use commented lines for the #2 suggestion?

swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Added Method Functionality

20 May 2020, 16:09

What is a Docstring?
A docstring is a string literal that occurs as the first statement in a module, function, class, or method definition. Such a docstring becomes the __doc__ special attribute of that object.
doesnt seem of much use, unless lexikos also creates corresponding docgen utils
User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Re: Added Method Functionality

20 May 2020, 19:34

@swagfag which I highly doubt he would do that
lexikos
Posts: 9553
Joined: 30 Sep 2013, 04:07
Contact:

Re: Added Method Functionality

22 May 2020, 20:29

1. Being a feature of many mature programming languages, this is an old idea that has been discussed before. For instance, https://www.autohotkey.com/boards/viewtopic.php?f=13&t=28150

My preferred method of dealing with this is to use convention and documentation. If you want to prevent users of your library from calling the method, make it awkward to call, tell them not to call it (or just what is acceptable to call), and let them deal with the consequences of ignoring your recommendations. If calling your private method is the only way to achieve what they want, let them do it. It's not like you can stop them from making a private method public.

ECMAScript 6 uses the prefix # for private fields. One possible implementation is to replace all such symbols at load time with unique values that only the compiler/interpreter knows. For example, inside class A, #x could internally be the string #A.x/89234, while in class B it could be #B.x/03248, and outside of a class it would be an error. The class name prefix allows it to be identified by a debugger (or enumerator if the field can be enumerated), while the random suffix prevents the user from using something like that["#A.x"] or that.%"#A.x"%.

As for making private methods awkward to call, one strategy is to use a hotstring to generate a prefix that can't be typed easily. For instance, in Object.ahk I used the prefix ← for private fields and a hotstring like :*:<-::← to type it (although this was actually just to avoid key conflicts).


2. In Visual Studio, when I hover over a variable, function or type name, I get a tooltip showing the content of an adjacent comment, which is usually documentation. I find this to be fairly effective. Sometimes the comments aren't documenting what I'm hovering over, but that's because they weren't written with this specific feature in mind. Perhaps the only benefit of using a standalone multi-line string in place of a comment is that the string is otherwise completely useless, whereas the comment might relate to something else. However, I think it carries the wrong meaning.

In any case, tools already exist to generate documentation from comments. Comments are fundamentally documentation; there is no need for another type of comment.
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Added Method Functionality

28 Sep 2021, 14:17

Delta Pythagorean wrote:
18 May 2020, 23:27
2.)
Documentation of a method inside a class is very useful at times. Python does this by having three double quotes just under the function definition:

Code: Select all

def my_function():
	"""I just display True so..."""
	return True
Doing something for AHK shouldn't be too hard right...? I'd say have some sort of meta function that defines the documentation of the methods.
if u insist on having the comment bearby the method declaration, with some caveats, its possible kinda:

Code: Select all

#Requires AutoHotkey v2.0-beta.1

class DocStrings {
	static __New() {
		defineMethodWithDocString(TargetObj, Method, docString := 'No documentation available.') {
			; needed in case there are going to be BOTH class and instance
			; method with the SAME name. ensures each method gets its own
			; unique doc string. CAVEAT: the type is going to be BoundFunc, 
			; instead of Func, which is what the type was going to be if u 
			; had defined the methods normally. MORE CAVEATS: checking 
			; A_ThisFunc will only get u the name of the BoundFunc, ie the 
			; class/prototype part of the string is missing.
			ClonedMethod := Method.Bind()

			try ; static: 'ClassName.MethodName'
				classString := TargetObj.Prototype.__Class '.' Method.Name
			catch ; instance: 'ClassName.Prototype.MethodName'
				classString := TargetObj.__Class '.Prototype.' Method.Name

			; overwrite Name with ours
			ClonedMethod.DefineProp('Name', {Get: this => classString})

			; add the docstring
			ClonedMethod.__doc__ := docString

			; defineMethod()
			TargetObj.DefineProp(Method.Name, {Call: ClonedMethod})
		}

		; "macros"
		static CLSS_MTD := defineMethodWithDocString.Bind(this)
		static INST_MTD := defineMethodWithDocString.Bind(this.Prototype)

		CLSS_MTD(method, 'A static class method.')
		INST_MTD(method, 'An instance method.')
		method(this) {
			MsgBox A_ThisFunc
		}
	}
}

DocStrings.method() ; 'method', instead of 'DocStrings.method'
MsgBox DocStrings.method.__doc__ ; 'A static class method.'
MsgBox DocStrings.method.Name ; 'DocStrings.method'

ds := DocStrings()
ds.method() ; 'method', instead of 'DocStrings.Prototype.method'
MsgBox ds.method.__doc__ ; 'An instance method.'
MsgBox ds.method.Name ; 'DocStrings.Prototype.method'

Delta Pythagorean wrote:
18 May 2020, 23:27
I have two ideas:

1.)
In a class there's all kinds of things you can do with a method, but sometimes you want to have private and public functions for people to use.
we want to:
  • make "privated" methods uncallable from outside the class(eg someObj.thePrivateMtd() should fail)
  • make "privated" methods callable from within class
  • make "privated" methods unenumerable from outside the class
  • make "privated" methods enumerable from within the class(doable but i didnt bother)
  • not have to write any weird shit unicode chars or whatever(ie keep writing code as though "private" actually was a keyword)
doable with a lot of overhead:

Code: Select all

#Requires AutoHotkey v2.0-beta.1

class Encapsulation {
	static __New() {
		PrivateMethods := Map()
		PrivateMethods.CaseSense := false ; ahk is case insensitive when it comes to variables and suchlike

		Proto := this.Prototype ; ie Encapsulation.Prototype
	
		; "macro"
		PRIVATE(methodName) {
			method := Proto.%methodName% ; retrieve the instance method funcreference
			Proto.DeleteProp(methodName) ; delete it from the prototype, so its no longer callable
			PrivateMethods[methodName] := method ; store it for our meta-__Call use
		}

		; this is something YOU have to write
		; =========================
		PRIVATE('privateMethodOne')
		PRIVATE('privateMethodTwo')
		; =========================

		Proto.DefineProp('__Call', {Call: __Call})
		__Call(this, undefinedMethodName, Args) {
			; stack can look like this:
			; C:\path\to\ur\script.ahk (27) : [__Call] stack := Error('', -2).Stack
			; C:\path\to\ur\script.ahk (57) : [Encapsulation.Prototype.publicMethod] this.privateMethod()
			; > Auto-execute

			stack := Error('', -2).Stack ; -2 to skip this meta-__Call entry
			; stack is now:
			; C:\path\to\ur\script.ahk (57) : [Encapsulation.Prototype.publicMethod] this.privateMethod()
			; > Auto-execute

			; parse the first line for the method designation between [ ], ie 'Encapsulation.Prototype.publicMethod'
			trueCaller := RegExReplace(stack, 's)^.*? \(\d+\) : \[([^\]]+)\] .*', '$1')

			for propName in Proto.OwnProps() ; we want to enumerate methods now
			{
				propDesc := Proto.GetOwnPropDesc(propName)
				if propDesc.HasProp('Call') ; is a method?
				{
					; is one of our public methods, calling an internal private one?
					if (trueCaller ~= this.__Class '\.Prototype\.' propName)
					{
						; yes, this is allowed, so retrieve the privatized instance method and invoke it
						return PrivateMethods[undefinedMethodName](this, Args*)
					}
				}
			}

			; the caller wasnt one of our public methods, but it could have been one
			; of our private methods calling another private method, so check it now

			; eg 'privateMethodOne' extracted from 'Encapsulation.Prototype.privateMethodOne'
			maybePrivateCaller := StrSplit(trueCaller, '.').Pop()

			; does such a private method actually exist?
			if PrivateMethods.Has(maybePrivateCaller)
			{
				; if so, then the invocation is permitted:
				; private methods may call other private methods
				return PrivateMethods[undefinedMethodName](this, Args*)
			}

			; otherwise, client is trying to invoke a privatized(or some other random nonexistent) method directly, so throw generic
			throw MethodError('This value of type "' Type(this) '" has no method named "' undefinedMethodName '".', -1)
		}	
	}

	publicMethod() {
		this.privateMethodOne()
	}

	privateMethodOne() {
		this.privateMethodTwo()
	}

	privateMethodTwo() {
		stack := Error().Stack
		MsgBox A_ThisFunc '`n`n=======`n`n' stack
	}
}

enc := Encapsulation()
enc.publicMethod() ; this should succeed, despite relying on private methods internally
enc.privateMethodOne() ; this should throw
enc.privateMethodTwo() ; this should throw
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Added Method Functionality

29 Sep 2021, 05:04

lexikos wrote:
22 May 2020, 20:29
1. Being a feature of many mature programming languages, this is an old idea that has been discussed before. For instance, viewtopic.php?f=13&t=28150

My preferred method of dealing with this is to use convention and documentation. If you want to prevent users of your library from calling the method, make it awkward to call, tell them not to call it (or just what is acceptable to call), and let them deal with the consequences of ignoring your recommendations. If calling your private method is the only way to achieve what they want, let them do it. It's not like you can stop them from making a private method public.
image.png
image.png (55.98 KiB) Viewed 2680 times
when has convention and documentation ever worked? if it did, there'd have been no need for v2 - we'd simply tell people "convention and documentation" and make all problems go away.
FuncObjects require prior assignment to a variable? convention my dude, read the docs.
u messed up percent signs in expressions for the umpteenth time today? convention, did u read the docs?
conventions only get u so far. its one thing telling someone they cant do something, its another thing actually preventing them from doing it(yes, i know they can come along and intentionally erase the private keyword, thats not the point)
the point is, u protect them from unintentionally shooting themselves in the foot. furthermore the IDE would assist in doing that, by not providing completions in the wrong contexts for example
lexikos wrote:
16 Feb 2017, 04:39
I don't believe it would be of much value to the majority of AutoHotkey users, but more to the point, I'm not interested in implementing it myself. You're welcome to do so (here's the source code) and see how much interest there is in adopting it.
damn, would if i could

Return to “Wish List”

Who is online

Users browsing this forum: No registered users and 30 guests