Updated to v2-a136. [list of 51 suggestions]

Discuss the future of the AutoHotkey language
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Updated to v2-119. [list of 47 suggestions]

Post by swagfag » 30 Jul 2020, 21:26

methods are defined on a separate, different object(called the prototype, accessed via randomClassybt.Prototype and instanceOfRandomClassinst_ybt.Base), so if u enumerate instanceOfRandomClassinst_ybt itself u get back nothing. if u enumerate instanceOfRandomClassinst_ybt.Base, u get the instance methods(again, not including any parent class', like mreq, Object, Class and Any)
masterpiece
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Updated to v2-119. [list of 47 suggestions]

Post by vvhitevvizard » 30 Jul 2020, 21:37

@swagfag I wasn't aware ur reply is on the 4th page so I kept adding stuff to my prev. post. Great explanation :thumbup: That killer-picture and ur explanation should be put into the documentation.

to resume:
For now, for json.dump I just add an additional loop calling .base.ownmethods. U defeated my #34, the only grumble left is obj.ownmethods to be extended in the future to additionally enumerate .base for methods so as to mirror (emulate) obj.ownprops mode. Updated #34 in OP (hid complaints under a spoiler).
In the past, I used the following syntax in AHK V2-103 to exploit fat-arrow function style with methods (=> wasn't supported for methods back then):
methodN:=()=>msgbox() and used some __init method to do other [instanced] methods initialization and that way props and methods (essentially, pointers to functions) were following the same suit.
40. in the future if more data structures are added, id want them to enumerate in a natural way that makes sense for them, not to satisfy an arbitrary constraint.
From that point of view, it makes sense. I feel the majority is against it. My decisiveness towards for-loop predictability is swayed by the arguments. Yet the list is my point of view, my wishlist - so for now I'll keep mine and add ur reasoning to the #40 with red color for others to judge.
46. i thought interpreter-altering directives were off the table. a better solution is ripping the bandaid off
Some ppl r against surgery :D well, we have added #required preprocessor directive and there will be others, and who knows, it might end up with a full-fledged preprocessor being added in the future, so having #syntax strict for now could suit as a transition phase helping ppl get used to no-commands mode.
now that i think about it, if adding extra special operators(eg * ^ & out) would simplify the implementation/provide more robust error detection, go for it.
at the very least it would serve to better document outparam usage
exactly. Also it helps to memorize parameters order. and for ppl who don't care -> simply initialize such vars to suppress warnings. And later on, if it got accepted by the majority, using out might become mandatory.
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Updated to v2-119. [list of 47 suggestions]

Post by vvhitevvizard » 31 Jul 2020, 00:34

48. thats faux-command syntax for u. because theres nothing to infer a get/set context, its interpreted as a feeble attempt at method/function call.
Good timing to set about writing "AHK V2: 100 reasons to hate command syntax" book. :D I suppose its worth being #48 then. :evil:

So if explicit expression syntax used by enclosing it in parenthesis, it works:

Code: Select all

try f.type ;f.%type%() (???). not expected
;another variant:
try
	f.type ;not expected
->

Code: Select all

try (f.type) ;OK
;
try
	(f.type) ;OK
BTW it doesn't allow try(f.type) ;syntax error (w.o a space) yet another control flow statement is not averse skipping that space: return(g)
Blasphemy! Discrepancy! Kill! Burn! Burn! :lol:

It reminds me (not an expression? its a function call then!..):

Code: Select all

g:=1
g && msgbox() ;ERROR: call to non-existent function
->

Code: Select all

(g) && msgbox() ;OK
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Updated to v2-119. [list of 47 suggestions]

Post by vvhitevvizard » 31 Jul 2020, 08:50

Basically the major issue of command and function syntax co-existence is the general expectation that follows other languages (C/CPP,D,E,F, Go and so on - the majority of them) design rule: a variable/object member's name with () following it executes the function/method with that name, a name w/o () is treated as a variable/object/pointer. And I can't help it - I keep shooting at my foot.
Also, var inside [explicit] expressions and outside them (after control flow statements, in the line beginning) processed differently and misleads even
more:

Code: Select all

f 1 ;function's call
f ? 1:0 ;variable's access
(f 1) ;variable's access

f(*)=>msgbox()
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Updated to v2-119. [list of 48 suggestions]

Post by vvhitevvizard » 01 Aug 2020, 03:34

I tried to clean up OP again. Added #48.
BTW, what does the community think of #24 blatant issue?
E.g., that bug (against expectation) swallows the whole line in here (just run it and see the parsing time error, don't mind runtime errors):

Code: Select all

(r:=dllcall("bcrypt.dll\BCryptCreateHash", 'ptr',o.p, "ptrp",h:=0, 'ptr',b1, 'uint',o.n1
	, 'ptr',(o.s) && o.b3, 'uint',(o.s) && strlen(o.s), 'uint',0)) && Error()
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Updated to v2-119. [list of 48 suggestions]

Post by vvhitevvizard » 01 Aug 2020, 13:17

1 more inconsistency with control flow statements. if (expr)=if(expr), but return (expr) is not the same as return(expr).

Code: Select all

f:=1
if(f){ ;OK
}
if (f){ ;OK
}
e()

e(){
	f:=1
	return(f) ;ERROR: call to non-existent function (unexpected)
	;return (f) ;OK
}
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Updated to v2-119. [list of 48 suggestions]

Post by vvhitevvizard » 01 Aug 2020, 18:47

Concerning V119's slowdown (https://www.autohotkey.com/boards/viewtopic.php?p=344631#p344631)
I found something. Run this with V2-118 and V2-119. 6% slowdown for average result (rightmost). Reproducible. Steady 6-7% (0.38mcs vs 0.41mcs) on each run. But unlike the test script in the link above, min result is not deteriorating. My wild guess its indicative of garbage collection, dynamic memory allocations, or something like that inside the interpreter. I benchmarked a complex AHK project (5-7k lines) that yields 12% slowdown on average. Something definitely went askew with the last update.

Code: Select all

#singleinstance force

dbg.reset(), num.Decimals:=4, _1:=_2:=b:=0
loop(n:=500000){
	dllcall('QueryPerformanceCounter', 'int64P',_1)
	dllcall('QueryPerformanceCounter', 'int64P',b) ; <-we measure this (well, we measure 3 dllcalls between snapshots). V119 is 6% as slow
	dllcall('QueryPerformanceCounter', 'int64P',_2),  dbg.add("dll call with 1 param", _1,_2)
}
msgbox(a_clipboard:=dbg.dump())

class dbg{ ;VER 1.2 by vvhitevvizard | REQ num, time
	static add(_s, _a, _b){ ;add current iteration time _s:checkpoint name, _a:start time, _b:stop time
		;a:min,b:max,c:counter,s:sum
		o:=dbg, d:=time.ticks2mcs(_b-_a)
		o.k.has(_s) ? (r:=o.k[_s], r.c++, r.s+=d, d<r.a && r.a:=d, d>r.b && r.b:=d) : o.k[_s]:={a:d,b:d,c:1,s:d}
	}static dump(){ ;print profiled data
		i:=0, s:=""
		for k,v in this.k
			s.= ++i ". " k " | " (v.c=1 ? num.r(v.a) : "min:" num.r(v.a) " max:" num.r(v.b) " avg:" num.r(v.s/v.c)) "`n"
		return s
	}
	static reset()=>this.k:=map()
}
class num{ ;v1.3.4
	static Decimals:=1 ;decimals for Round()
	static r(_n)=>round(_n, num.Decimals)
}
class time{ ;v1.1.3
	static ticks2mcs(_t){ ;perf counter ticks to mcs (microseconds)
		;The freq of the perf counter is fixed at system boot and is consistent across all processors
			;We query it upon app init and cache
		static f:=0
		;If the installed hardware supports a high-resolution perf counter, the retval is nonzero
		(f) || 	dllcall("QueryPerformanceFrequency", 'int64P',f)
		;To guard against loss-of-precision, we convert to microseconds *before* dividing by ticks-per-second:
		return(_t*1000000/f)
	}
}
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Updated to v2-119. [list of 48 suggestions]

Post by Helgef » 02 Aug 2020, 00:27

Reproducible
Not on my pc.
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Updated to v2-119. [list of 48 suggestions]

Post by vvhitevvizard » 03 Aug 2020, 06:02

Helgef wrote:
02 Aug 2020, 00:27
Not on my pc.
I mostly use Win 10 LTSB/LTSC (stable but reduced functionality builds). I tested on 4 different machines (2 desktops and 2 notebooks), gonna test Win7 OS sandboxed in a virtual machine. Could u please post ur output for the latest script and for the test2.7z one?
Addition to #48. In C, return (expr); vs return expr; vs return(expr);. Its dependent upon personal taste: all the three variants r legit.
  • Some coding guidelines consider using braces a bad practice, yet other stylistic conventions require them.
  • Whitespace (or space(s)/tab(s) at least) between non-literal symbols is optional.
  • Originally old convention C had parenthesis mandatory for returned values.
  • Parenthesis can be used to define return as a macro and then insert some logging code to trace all the return () statements.
The same goes for case, throw and probably some others.

back to AHK V2: we've got this inconsistency:

Code: Select all

try f ;f considered as a function call
return f ;f considered as a variable
Making parenthesis mandatory for every expression (condition, returned value) following a statement keyword: would it reduce the parser's complexity and increase performance?
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Updated to v2-119. [list of 48 suggestions]

Post by Helgef » 03 Aug 2020, 11:21

Code: Select all

2.0-a118-1e6cd8a1
1. dll call with 1 param | min:0.4000 max:22.9000 avg:0.5307

Code: Select all

2.0-a119-179d27fd
1. dll call with 1 param | min:0.4000 max:1673.5000 avg:0.5172
This test code isn't very good. Disregarding that the performance of dllcall is a pretty poor metric for a118 vs a119 performance, you shouldn't measure one thing 500000 times, you should measure doing the one thing 500000 times one time.
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Updated to v2-119. [list of 48 suggestions]

Post by vvhitevvizard » 04 Aug 2020, 05:25

Hmm, ur results show 3% delta (0.531 vs 0.517) with V119 being faster than v118. Is that correct? If it is then yeah that test is unreliable. What OS u use?
Helgef wrote:
03 Aug 2020, 11:21
you should measure doing the one thing 500000 times one time.
That's what I do in the previous test suit test2.7z by measuring 1 relatively huge chunk of real-world data processing. Please share what results u've got for it as well :D
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Updated to v2-119. [list of 48 suggestions]

Post by Helgef » 04 Aug 2020, 05:50

Correct. I didn't run the other test (yet, maybe)
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Updated to v2-119. [list of 48 suggestions]

Post by vvhitevvizard » 06 Aug 2020, 17:56

49.
https://lexikos.github.io/v2/docs/commands/Trim.htm
Any string value or variable. Numbers are not supported.

Code: Select all

s:=7700.4 ;type=float
msgbox(s:=ltrim(s,"7"))
output:

Code: Select all

00.3999999999996 ;type=string
So it silently converts a pure number (integer/float) into a string before trimming it.
Either the documentation needs to be updated to mention that behavior or the command trim|ltrim|rtrim to be changed to throw ("expected a number but got a string" or something like that).
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Updated to v2-119. [list of 48 suggestions]

Post by Helgef » 07 Aug 2020, 06:18

This is the conventional behaviour.
Caching wrote:AutoHotkey automatically converts between numbers and strings in cases like "Value is " myNumber and MsgBox myNumber.
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Updated to v2-119. [list of 48 suggestions]

Post by vvhitevvizard » 07 Aug 2020, 10:24

Then this documentation article needs to be updated:
Any string value or variable. Numbers are not supported. -> Any string/numeric value or variable.
50.
Debugging facility. To add ObjRefs(obj) returning object's reference-counting to replace this undocumented object's data use with a built-in function:

Code: Select all

objrefs(_o)=>isobject(_o) ? numget(objptr(_o), 8, 'int64')-1:0 ;get obj ref counter

;test
b:=aaa.new()
msgbox(objrefs(b)) ;=1
c:=b
msgbox(objrefs(b) "|" objrefs(c)) ;=2|2
b:=""
msgbox(objrefs(b) "|" objrefs(c)) ;=0|2

class aaa{
	__new()=>this
}
but this is not reliable cuz for oo:={}, oo.n:=aaa.new(), p:=objptr(oo.n) (oo.n expr -> "aaa" class object) returns unexpected 2:

Code: Select all

objrefs(_o)=>isobject(_o) ? numget(objptr(_o), 8, 'int64')-1:0 ;get obj ref counter
objrefs2(byref _o)=>isobject(_o) ? numget(objptr(_o), 8, 'int64'):0 ;ver2: get obj ref counter
oo:={}
n:=aaa.new()
msgbox(objrefs(n)) ;=1 (OK)
oo.n:=aaa.new()
msgbox(objrefs(oo.n)) ;=2 (unexpected)
msgbox(objrefs2(oo.n)) ;=3 (unexpected, byref'ed obj's ptr passed to a function increased obj ref count OUTSIDE that function)

class aaa{
	__new()=>this
	__delete()=>msgbox("__delete()")
}
So built-in ObjRefs(obj) could solve it all.
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Updated to v2-119. [list of 50 suggestions]

Post by Helgef » 07 Aug 2020, 12:41

We already have ObjAddRef / ObjRelease if we need that.
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Updated to v2-119. [list of 50 suggestions]

Post by vvhitevvizard » 07 Aug 2020, 15:37

Yes but what if a reference counter is unknown and/or the culprits that increase it r unknown as well, and we need to obtain the count (for debugging purpose)? For a complex project it might be very hard to manually keep every reference in check.
Having the ability to count the object's references would help A LOT. For example, Python has that functionality via garbage collector's .get_referrers() and system's .getrefcount().
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Updated to v2-119. [list of 50 suggestions]

Post by Helgef » 08 Aug 2020, 03:34

Having the ability to count the object's references would help A LOT.
As I said, use objaddref/release,

Code: Select all

o := {}
objaddref objptr(o)
count := objrelease(objptr(o))
msgbox count
Yes but what if a reference counter is unknown and/or the culprits that increase it r unknown as well, and...
I don't know what you mean.
Then this documentation article needs to be updated:
Any string value or variable. Numbers are not supported. -> Any string/numeric value or variable.
I disagree. Number are not supported means that it will not consider numeric input as a number and return a number, as you saw, numeric input is converted to string and returned as string.

Cheers.
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Updated to v2-119. [list of 50 suggestions]

Post by vvhitevvizard » 24 Aug 2020, 03:08

objrelease does return the references number. Well, one can live with it for debugging purpose by adding a new object reference and then removing it to get the function's return value. ok.
Number are not supported means that it will not consider numeric input as a number and return a number
We talk from different points of view - developer and user. Not supported means not maintained (throws an exception, etc). Replacing it with "numbers r converted to strings" would clarify the function's description.
I tried V122. Well, mysterious slowdown is not fixed. still 4% (or more) as slow compared to V118. Maybe some compiler options were changed, e.g. optimize for speed flags to optimized for size flags?
User avatar
vvhitevvizard
Posts: 454
Joined: 25 Nov 2018, 10:15
Location: Russia

Re: Updated to v2-119. [list of 50 suggestions]

Post by vvhitevvizard » 14 Mar 2021, 06:45

Recent updates (in)directly addressed the following issues (open post's paragraphs):
#3 (partially), #12 (on paper), #32, #34, #45, #48 (partially - somewhat less unexpected errors).
Correct me if I missed something.

I used the same real-world use benchmark as before (the link to the script in OP).
Lower is better. First number(row) is for json.load json str->AHK obj conversion using DLL calls for SSE2 assembly code and huge 250 tokens AHK expression, Second number(row) is for json.dump AHK obj->json str conversion using recursion.

Code: Select all

;6922/3484  v2-129 (1st number 7% as slow compared to V2-127)
;6485/3640  v2-127
;6140/3766  v2-122
;6390/3985  v2-119 (1st number 9% as slow compared to V2-118. it wasn't addressed yet)
;5875/3565  v2-118
;5907/3704  v2-116
;5950/3937  v2-115
;5750|3984  v2-113
;6015 |3580  v2-108
If someone interested, I can link the updated (v129 compatible) version of json.dump/json.load functions.
Last edited by vvhitevvizard on 16 Mar 2021, 04:01, edited 2 times in total.
Post Reply

Return to “AutoHotkey Development”