I use the same real-world benchmark (the link at the bottom of the post).
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.
;7000/3906 v2-136
;6891/4015 v2-133
;7015/3906 v2-131
;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
AHK V2 development shows great progress. I piled up a list of bugs/typos/my point of view suggestions accumulated recently. I prefer this format of keeping it all together in 1 updating post instead of creating multiple topics. The post will be revisited again and again by re-phrasing/clarifying the root of the problems, adding the community's opinions, removing the irrelevant and misleading stuff (errors happen quite often), adding more indicative short examples and half-way solutions, hiding resolved issues under spoilers and so on. Some of my points might be irrelevant (as proved by @Helgef, @swagfag, @nnnik, @lexikos) and I keep discarding them. Pls join that discussion and say what u think of some suggestions, add urs.
Most of my previous and following points propose command name's/syntax slight changes/addons/removals and focus on bringing more uniformity to the syntax: 5, 6, 13, 15, 25, 26, 27, 29
Some others concern debugging facility extension and seem to be not so hard to implement: 2, 3, 35, 36, 50. These, I believe, would greatly help with testing and debugging new alpha versions.
1. (brilliantly resolved in V2-114)
1. Cant wait to see removal of 255 tokens limit for an expression. Moving all the if/else commands {} block logic to parenthesis () expression dramatically increases performance. E.g.
I guess "expression is too long" issue is due to some constant limiting it artificially. Would be cool to have it set for a higher threshold at least.
2. Ability to get global AHK vars (user and built-in) by enumerating them or something. And same for local ones. One step closer to managing namespaces.
e.g. listvars could take arguments and return some array or map instead of printing out in a window. Tbh, its current realization is totally useless for my taste: I can't use its output programmatically!
I recall in the past there were some hackery topics of patching AHK interpreter's binary code segment at runtime just to get these vars for further analyzing programmatically.
3. (call stack - partially addressed in v2-125)
3. We've got A_LineNumber, A_ThisFunc, A_LineFile, A_ThisLabel.
Request to add a similar command or expand the existing ones to expose callee's line number and function name (in other words, to expose calls' stack similar to exception()).
More introspection facilities like this would be very useful for debugging and logging purpose!
v2-125: Added Stack property to Error objects, since then it's possible to work around by intentionally throwing to get the call stack at the time the Error object was constructed.
4. (resolved in V2-113)
4. (bug?) gui control opt "Background" with illegal value (e.g. "BackgroundGrayWhite") returns Black color (returns 0) instead of throwing an exception. Its definitely a user's typo here, we don't want AHK to skip it.
5. Also it would be nice to have a shorthand for "background" keyword like "bg" or "b". Similar to "c" for "color".
Essentially, "background" and "section" r the only keywords that don't have a shorthand:
;an array of options for gui.add (for its 2nd parameter)
["section background" b, "xs r1 w" w " background" b, "xs background" b, "xs r1 w" w " background" b]
6. (bug?) if an object has __Enum method, it should return something for for-in loop and don't throw. I found irregular behavior for Map's extension classes:
class dbg{
}
class dbg2 extends Map{
}
msgbox("dbg.prototype.HasMethod('__Enum'):" dbg.prototype.HasMethod("__Enum")) ;0
msgbox("dbg2.prototype.HasMethod('__Enum'):" dbg2.prototype.HasMethod("__Enum")) ;1
for k,v in dbg2.prototype ;error: Type Mismatch (v118). error: Expected a Map but got a Prototype (v129)
msgbox("here")
The point is I just used a general recursive cycling thru all the exposed properties/nested objects of an object derived from Map.
for k,v in (_o.hasmethod("__Enum") ? _o:_o.ownprops())
Function logic here is if something of built-in matter has __Enum then it can be enumerated via regular for k,v in obj and it SHOULD expose something w/o throwing. Otherwise we fall back to browsing thru obj's properties via for k,v in obj.ownprops().
7.File and some other built-in objects should expose more properties for an enumeration.
Usage? Self-logging script. File.pos, etc In the case of file. One could just do a general and simplefor-in loop thru classes/global vars to save the current script's state. File object might be not the best example. But exposing as many properties as possible is not a bad thing for built-in objects.
8. (irrelevant)
8. (bug?) keyword "local" as the 1st function line. (v2-112 run-time error)
If the function's first line is the word "local", all variable references are assumed to be local unless they are declared as global inside the function. https://lexikos.github.io/v2/docs/Functions.htm#Local
g:=gui.new(), g.add("Edit","w250","text"), g.show()
box()
box(){
local ;!!!
;next line gives an error: "this var has not been assigned a value"
g:=gui.new()
g.add("Edit","w250","TEXT2"), g.show()
g.show()
}
the first function line is keyword local and we r trying to assign a value further on.
So it wants me to explicitly declare vars as local now? a single local keyword + assignments route is not allowed any longer?
9. Function to support Map creation.
To get the idea of what I mean, in an example we split a string with ";" and ":" delimiters:
s:="
(
margin:1.0pt;
text-indent:8.5pt;
font-family:"Verdana";
)"
t:=Map(StrSplit(s, [";",":"])*) ;relatively slow
;and throws run-time errors on empty array members corresponding to key-names instead of ignoring them..
;it should also left-trim/right-trim white space characters like \n\r\t for key-names.
Key idea is to speed up this basic map/str conversion functionality.
It should be a lot faster doing this with a new separate command like StrSplitM with native c++ code.
10. (resolved in V2-113)
10. (bug?) While typing, I accidentally inserted a command from NSIS (abort) and...
msgbox("text")
abort ;OK. Error:Call to non-existent function
11. (irrelevant)
11.for k in obj acts different in v2-100 and v2-108. the former returns an index, the latter returns a key.
Also there is no mention of removed .Count() for objects as well.
When upgrading to a newer AHK 2 alpha version, we try to browse thru the change notes at first to get an idea where to pinpoint errors. Sometimes any mention of such changes is the last resort to figure out why the script behaves wrong with the new interpreter.
All that requires better detailing for version history. Not a detailing reasoning why and an extensive examples list but providing just a mentioning is a must.
12. (resolved in v128 and v131)
12. local/global keywords for fat-arrow functions. In general it needs some vars declaration ability within an expression of fat-arrow. To be able to inline local vars usage. Right now I cannot use local variables inside fat-arrow function that declared at global scope.
So I have to resort to using a "hack" of allocating local vars in the args list, e.g.:
Or to make fat-arrow functions to be assume-localunconditionally.
13.NumGet to have a mode similar to NumPut "type1",output var1, "typeN",output varN ... address,offset. So it could automatically read multiple values by reading a value and changing the offset to the next one...
r:={}, b:=bufferalloc(16)
numget('float',r.x, 'float',r.y, 'float',r.w, 'float',r.h, b)
14. Recently I found this tendency of super-long keywords adoption. An average word in English is just 4.5 letters and computer languages go even further using some sort of abbreviations. For example, the majority of Basic's keywords is 3-5 letters long: dim, rnd, wend... Who knows, in the future AHK might get a command line interpreter mode like Python - the shorter keywords the better typing performance, and good stuff for inliners.
Recent updates have brought this monstrosity: varsetstrcapacity: 4 words 17 letters within 1 keyword, why so long? Why not SetStrCapacity, StrCapacity or something?
Another monster is GetOwnPropDesc. These Own* methods r too long to my liking, I would get rid of "Own" suffix. GetProp -> DefineProp, GetMethod -> DefineMethod pairs seem to be more appropriate.
And enumerators could be reduced down to .Methods() and .Props(): s and () at their end implies that they r enumerators.
15.
Further exploiting the conversion to objects for built-in function sets. Built-in objects system to be unified and consistent!. fileopen() -> file.new() bufferalloc() -> buffer.new()
maybe some cool syntax for StrSplit as well as long as it creates Array object. Array.FromStr() and Array.ToStr() (missing join, the opposite to StrSplit). And same for Map: Map.FromStr, Map.ToStr... U name it, I just brainstorming here
16. (resolved in V2-113)
16. (bug?)
Scenario: some nested function recursively calls itself. And it got overlooked by the script's author. Point is the stuff silently crashes breaking thru even scripts's critical error terminating route, not reaching catch/onerror/onexit control checkpoints. I would like to have a #warn setting to do some sanity check at loading time for presence of recursive calls to warn. In case a programmer let it be by mistake. Just in case. Recursion is a type of programming art not just anyone can master w/o dramatic performance drop and thus it rarely used in actual practice.
Or any changes to the interpreter to make it able to intercept at runtime _before_ critical stack overflow happened.
17. After having read the AHK v2 subforum, I came across with @Lexikos talking about BufferFrom(Ptr, Size) or similar command for memory range re-usage. This one would be very dangerous, possible to Page faults and other severe errors, but sometimes while working with huge memory arrays, re-usage instead of copying is a matter of life and death.
Lets imagine, we have a huge memory region created not by AHK's BufferAlloc/File.RawRead or others. It might be an aftereffect of some external library call. We do have a pointer and we know a current size of that region and we want to use Buffer object facilities to control its boundaries and to tackle it using NumPut or other Buffer-aware functions. And we don't wanna waste CPU and memory resources by copying that memory region. b:=BufferFrom(memory_addr, current_length) and voila! We've just reduced our carbon dioxide emission level! Cant wait for such a command to be implemented.
18.Preprocessor. Macro.
Why is this so important? Nowdays even assemblers or simple scripting languages like NSIS have an extensive preprocessor system. Google "ahk+preprocessor" https://www.google.com/search?sxsrf=ALe ... CAw&uact=5 - hundred requests - might be the most sought-after feature to be implemented.
in the simplest form,
#define var_name
#if defined var_name
#else
#endif
would allow to have #include files cross-including each other w/o having troubles of multiple assignments/class definitions!
Basic pre-processor in its simplest form
#def const_name or #def const_name literal_value pre-processor directives. Special namespace for pre-processor constants that exists at load-time only. For starters, no text substitution/expansion, just constants for #if(not)def directives.
The very basic conditional preprocessing with positional directives would be not so hard to implement after all. The basic pre-processor I touch upon knows nothing of class declarations or other language statements, it processes only its own directives (lines starting with a few # directives #ifdef#ifnotdef#else#endif and #include) and considers all other lines as unparsable literal strings. In my view, It takes precedence in time right after (or concurrently, if we take into consideration some optimizations) the script got filtered out of comments/whitespace and continuation blocks r concatenated but before load-time pass that gathers declarations and do other syntax parsing.
And in the future, in case the language would have macros, it would require multiple passes until all the macro "expanded" at load-time.
one more example of preprocessor conditional branching
Idea is for library functions to call some outer class's methods only if they r "enabled" (the corresponding methods/functions #included in the script), e.g.:
;somewhere in auto-execute code we figure out that logger class is missing so we dynamically create a class's dummy method(s):
isset(&logger) || logger:={print:(*)=>}
...
;somewhere in included library's function/method:
logger.print("blahblahblah") ;now its unconditional. we just call it knowing it's either a full method or a dummy one doing nothing.
But having pre-processor's #ifdefined directive to conditionally opt between the current script lines (effectively, having several class declarations and other language constructions not possible at load-time and run-time) depending on the situation would be much better.
At the moment having several includes of the same class declarations leads to a load-time error with no way around it. That somewhat impedes script libraries development. Ideally it should be:
#ifdef conditional preprocessing and AHK command line argument allowing to define preprocessor's constants (e.g. /defDEBUG or /defDBGLEVEL=4) would enable to run verbose/debug/info/release/any other special builds of the same script.
allows to have a brief and tidy code to be expanded into multiple AHK instructions at _load time_. Sometimes it performs dramatically faster than nested functions/closures/loops/etc.
All in all, preprocessor+macro is a language inside language with infinite posibilities and it won't hurt run-time performance.
19.loop keyword keyword keyword annoyance. Its so AHK1ish!
I recall a few alpha versions had LoopX commands coalesced: LoopParse, LoopFile etc. Loved it. Why? It facilitates code searching/replacing, it would allow loopsomething to be a function so one could insert it inside an expression...
20. Speaking of commands. Its cool to have function's call f() to be shorthanded to command syntax like the 2nd line in the example:
but there is no opportunity to use remaining AHK commands as/in-place-of function calls or operators...
For example, let's look at the Python's loops inside square brackets: [f(x) for x in xs if condition]
def list_comprehension(xs):
result = []
for x in xs:
if condition:
result.append(f(x))
return result
Python syntax is specific, but what I mean is having an ability to add loop, break, throw in expressions...
For readability, some ppl prefer each command to be on a separate line, but others would inline everything!
21. (not possible due to the existing line concatenation rule)
21. I found myself shorthanding this keyword pretty often inside methods:
22. Refactoring... .exe compiler idea. NSIS is the only full-fledged script language competing with AHK by generating the smallest possible compiled standalone file: some standalone executable files could be as small as 25-40KB! Its done by separating interpreter's code into dlls and including only needed at "compile" time. NSIS script is not compiled into machine instructions or some pseudo-code, it remains in .exe as a source with removed var/subroutine names/comments/etc - same as AHK.
(here we've got some "built-in" and 3rd party libraries like 7z support)
I suppose like 1/3 of AHK .exe footprint is used by regexp lib_pcre, another quarter used by gui, 1/7 used by mouse functions, registry support... Imagine some compiled script not using this or that could be much much lighter in size.
23. Zero index for arrays. Recent versions made it mandatory to use indexing starting from 1. And [0] is an illegal index now. Alas, it does require additional work converting old scripts and worsens cross-language tasks. But also I do understand that having zero index reserved fits perfectly for many usage cases enabling the simplicity and power of boolean logic like in this case:
My point here is ok, new syntax, but make sure that all the remnants of old style zero indexing r not available by default.
Please remove Match[0] (equal to a whole found string). There is an alternative Match.len(0) anyways.
Continuation section bug - fixed in v2-a134
24. (bug) Concerning line continuation section start mark. https://lexikos.github.io/v2/docs/Scripts.htm#continuation If a line begins with a non-escaped open parenthesis ((), it is considered to be the start of a continuation section unless there is a closing parenthesis ()) on the same line.
That brings issues of mixing continuation section with expressions divided by continuation prefix:
In the above snippet one would expect Line 1 is completed and not connected to line 2,
Line 2 starts with an expression in parenthesis (function call with 2 args) and that expression's result used as 1st operand of && operator.
When a regular continuation section starts, it looks like
Var := "
(
Line 1 of the text.
Line 2 of the text. By default, a linefeed (`n) is present between lines.
)"
so ( of a continuation section's start mark is a solo opening or closing parenthesis and not followed by a variable or operator or other stuff we expect to be within an expression.
The current state leads to the following preprocessor printout (the same behavior with AHK v2 builds 103,108,115):
Error at line 1.
Line Text: a:=1, 0)) && a:=2
Error: missing ")"
WOW. In "Line Text" printout (b(0 disappeared! between lines 1 and 3. So AHK simply ignored leftovers that followed opening (. That's totally counter-intuitive.
another example
that bug swallows the whole line in here (load time error):
Request: In order to let lines starting with an expression to co-exist with continuation sections, start mark for the latter should be considered only when its a solo parenthesis on the line. And the closing section mark to be ) or )", )', )` respectively.
25. (resolved in v2-117)
25. Alternative to IsSet for undeclared classes test w/o throwing at runtime and w/o warnings at loading time. Imagine there r multiple #include commands and the script has to figure out whether a class declaration is included or not.
We don't have a preprocessor's #def .. #ifdef implemented [yet], so the current kludge I use:
;uncomment the next 2 lines with class declaration or 1 line with an object declaration
;class classname{
;}
;classname:={}
try
isset(classname) && msgbox("classname exists, type: object") ;never triggers for a class, trigger for an object
catch{
msgbox("classname exists, type: class") ;it actually triggers if class classname DOES exist
}
26. Similar to 25, ability to test whether a gui control is "destroyed" w/o throwing.
27. Naming inconsistency. Some built-in functions use Len prefix, other properties have a Length moniker. e.g. StrLen, Match.Len() vs File.Length, Array.Length. For consistency, I would prefer unified Len everywhere.
28.Callback for regexreplace. So that it would be possible to exploit a custom function instead of replace argument's string pattern.
NB: Not to confuse Callback with the existing Callout debugging facility!
Real-world use of that functionality (regexf function). Example does decimal html entities -> unicode) conversion:
30. When we assign a "text" to a gc control, AHK automatically measures its extents and resizes it accordingly. It would be logical when changing text style/etc to have the same auto-functionality. And ability to disable any autosizing at all like setting or not setting "AutoSize" attribute for gui.show().
Example (I used a snippet from a singlet class I use for miscellaneous gui utility functions). Uncomment 4th line:
g:=gui.new()
gc:=g.add("text","", s:="The quick brown fox jumps over the lazy dog.")
gc.setfont("bold")
;win.autosize(gc) ;uncomment this line
g.show("AutoSize")
class win{
static autosize(_g)=>(o:=this, win.textmeasure(_g), _g.move(,,o.tw,o.th))
;thats how AHK calculates the initial size of a text control for gui.add("text") when none is provided
;NB: (cg.getpos(,,w,h), w) is much faster: 2us vs 60us but it doesnt address all the issues
;_g:gui control object, _s:text. unset->use gc's val
static textmeasure(_g, _s:=unset){
local g,c,d,v, rc,w,h ;g:gc, c:hdc, d:font, v:tmp, rc:RECT, w,h:width,height
;hnd=0:retrieves the DC for the entire screen
o:=this, c:=dllcall("GetDC", 'ptr',g:=_g.hwnd)
;get the font based on the control itself. 0x31= WM_GETFONT. 0:the control is using the system font
(d:=(v:=sendmessage(0x31,,, ,"ahk_id" g)) ? dllcall("SelectObject", 'ptr',c, 'ptr',v):0)
;BTW, AHK gives "ButtonN" classNN for CheckBox, "ComboBoxN" for DDL
;-1:null-terminated str, 0x440:DT_CALCRECT=400|DT_EXPANDTABS=40 ;|DT_SINGLELINE=20
o.th:=dllcall("DrawText", 'ptr',c, 'str',isset(_s) ? _s:(_g.type="edit" ? _g.value:_g.text)
, 'int',-1, 'ptr',rc:=bufferalloc(16), 'uint',0x440)
o.tw:=(numget(rc,8,'int')-numget(rc,0,'int')) ;width=rc.right-rc.left
(d) && dllcall("SelectObject", 'ptr',c, 'ptr',d), dllcall("ReleaseDC", 'ptr',g, 'ptr',c)
}
}
BTW, C# has the following settings for gui controls:
etc
31. Continuation of the previous idea. gui.add("text","xp xs+3 w45 cWhite") syntax might be handy for simple gui control layouts, but for complex ones one might wanna populate gui controls programmatically.
So having additional commands changing gc style similar to recently added gc.getpos(x,y,w,h) is reasonably necessary: gc.setpos(), gc.settextstyle( [bold/italic/other flags,size, kerning/etc]), gc.setcolor(color [,background]), gc.autosize, gc.settextalign(), gc.padding, gc.marginx, gc.marginy, etc.
IMHO, both "text string of params" and "set of methods" approaches could co-exist for a while until the former to be phased out.
32. (resolved in V2-128)
32. Concerning another AHK's quirk mitigation: runtime checkup for a passed byref argument not being a variable. There is an existing IsByRef function already, but manually providing a checkup of every passed argument for being a not valid "expression" like obj.property is a pain.
Having an ability to enable/disable that check for all the byref arguments globally with 1 #warn directive would be great. If it doesn't dramatically hurt runtime performance ofc.
UPD: Resolved in v2-128 by introducing VarRef operator & and new automatic check-ups.
33. (v2-132 touches upon a matter)
this one is a general observation concerning non-preprocessor directives system: some in form of #command, others in form of a_variable. No uniformity here.
#SingleInstance force
#KeyHistory 0
a_listlines:=0, a_iconhidden:=1, a_fileencoding:="UTF-8"
;...
Due to preprocessor's stage 2 (or more?) passes, many language statements r put into effect before the script begins running (e.g. class declarations) and there is no principal difference between most of them - some r read-only, some r put into effect only once and before runtime, but there is no unified naming convention. For example, could #hotkeymodifiertimeout be a_hotkeymodifiertimeout? Or a_listlines be #listlines? I suppose, they were made this way due to AHK historically progressed by piling up more and more commands, written by different people, w/o actually following general design rules. Nowadays not having a unified system makes a learning curve more steep. Ideally, similar to other languages, only lexemes that active at the very first preprocessor's pass should have # prefix.
34. my complaints got resolved by @swagfag
.ownmethods() returns no methods for instanced class objects. it works for type="Class" and class.prototype, but not for instanced objects. It is expected to act the same way as .ownprops() and be effective for object's methods as well.
For the last 3 years I used to exploit a small utility (json.dump method) I wrote and keep updating for pretty-printing out AHK's objects/arrays/maps/all the other objects.
34 (II). also, it was later resolved in v2-128 by combining methods and props namespace
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.
35. Additional debug info. To let a script know of how much time (in milliseconds) pre-processing took. It might be A_LoadingTime built-in variable denoting a tick count of reaching WinMain() of AHK interpreter and preprocessor start, A_RunTime denoting a tick count of the beginning of script's run-time.
msgbox("preprocessing took " A_LoadingTime-A_RunTime " ms")
It would be a good instrument for assessing the interpreter's and script's code preprocess-time optimizations within a script.
36. Alternative to A_TickCount returning microseconds. A_TickCountExt? A_McsCount?
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)
}
Every split microsecond matters: having this built-in would increase accuracy of measurements: see https://www.autohotkey.com/boards/viewtopic.php?p=344851#p34485137. Idea of object member (expression) to be passed to a function for indirect assignment as if it was a variable, making &obj.prop and &-77 (pure number), &"string" possible. AHK would alleviate this frequently mentioned issue by using a temporary variable secretly. a.object.property use for dllcall:
a) working:
The reasoning of that quirk is known: object.property, being an expression, cannot receive a value directly.
Could we have some preprocessor's latent propagation of additional commands: allocating a temporary_variable for .property and committing obj.p:=temporary_variable right after the function call:
Before such an implementation, dllcall should flag an error for 'ptrP',obj.p. b.VarRef& use with object.property. Same as above. AHK creates a temp var to be used as an output, assigns it obj.prop value, calls a function and then assigns returned value of the var back to obj.prop.
Along with being compliant to the function's VarRef requirement and being shorthand, I want to use a temporary short-term life (just for the callee time line) variable w/o allocating any name to it within the caller's namespace. Yes, discarding any output to that var on purpose. *()=>expr creates an anonymous function's reference. *&(expr) could create a variable reference the same anonymous way. We might assign the reference to some variable function(ref:=&(expr)), otherwise the anonymously created variable would be discarded after the call.
---
So the documentation would contain just a note warning about some possible performance issues
in place of "known limitation" of total inability to use & with object members.
38. a namespace management syntax ideas
PHP-like: declaring globals inside the included file as belonging to namespace utils:
so that every variable declared as global would become utils:variable (utils_variable, whatever) for AHK's inner logic. And it is totally transparent for the code inside namespace {} brackets with AHK transparently pre-pending namespace prefix to vars/classes declaration.
And the script code located outside utils.ahk2 to address those vars/classes/functions as utils.name (utils:name, etc)
Python-like:
Declaring a namespace just for a function and also renaming it on the fly.
That would greatly help to resolve global var naming conflicts.
39.#requires
I can't apprehend what syntax is required to make some special script version run ONLY with the exact alpha version. I tried
But it still runs on V2-a116 w/o that special warning/error. The point is one might want an exact version to be defined, or the maximum version in addition to.
Request: ability to set a range, e.g. v2.0-a103, v2.0-a107 with comma or just whitespace between version numbers. E.g.:
#requires AutoHotkey v2 ;doesnt work with AHK V1 or AHK V3, tries to run with any AHK V2 version
#requires AutoHotkey v2-a116 ;works with this exact AHK V2 version ONLY.
#requires AutoHotkey v2-a116 V2-a119 ;expects only this range of AHK versions.
40. (resolved in v2-130)
40. Enumerators. One-parameter mode for array returns a value yet for map it returns a key. NB: the majority seems to be against this change. But its my wishlist so I keep my reasoning here:
a:=["aa","bb"]
b:=map("a","aa","b","bb")
for v in a
msgbox(v)
for v in b
msgbox(v)
For array enumerator to return value is quite convenient cuz key can be obtained via A_INDEX. It is expected that map would return value as well.
Was it made on purpose to differentiate array from map? In my opinion, both have similar access operator [] and this inconsistency with one-parameter for-loop might cause many unexpected issues. We just need a unified (expected) rule here.
So my try to bring it to consistency is to propose a 1-var for-loop to return a key (essentially only changing for k in array implementation (but not for k,v in array or both for-loop for map), it forces to use 2-vars instead of 1 for old for k in array behavior). It would make things plain simple and logical: 1-var for-loop returns keys always, 2-var for-loop returns keys,values.
The majority of script writers uses 2-vars mode anyways so the transition might be not so painful. It may be too late to change this behavior, or may be not - we have this opportunity while being in an alpha stage. Such quirks will be an eternal source of frustration. alternative opinion (to let it as is):
@swagfag: it doesnt matter that its not consistent with Map - it doesn't have to be; a Map is a Map and an Array is an Array, theyre different data structures. 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.
41.Variadic function/method vs (essentially) variadic syntax __call meta-function. __call(_a*) returns array of argument(s) nested in another array (unexpected).
An agreement required: for all the functions/methods to have variadic syntax if they return a variable number of parameters. Also that
requires that old and future built-in functions to have variadic params* to be the last in the parameters list.
see more details
I express this expectation from a user point of view:
#1 function(_a*) returns array of parameters(s) Syntax here (_a*) (OK).
documentation says: Variadic function can accept arrays
#2 __call(_a*) returns array of argument(s) nested in another array (unexpected). https://lexikos.github.io/v2/docs/Objects.htm#Meta_Functions
documentation says: __Call(Name, Params): Name:The name of the property or method, Params:An Array of parameters.
So basically accepting array as the only parameter is the same as declaring parameters list variadic.
I used the same syntax (_a*) for name+array of parameters. It was accepted but the result is unexpected.
If I use (_a) to address array of arguments it works but the inconsistency is what makes me restless. __call should either become a regular variadic function or simply ignore * following it instead of creating a wrapping array around it.
;Include my json.dump utility from the #34
t:=test.new()
t.f(77)
t.c(77)
class test{
f(_a*)=>msgbox("1:" json.dump(_a))
__call(_a*)=>msgbox("2:" json.dump(_a))
__new()=>this
}
@swagfag: Could be done if __Set(Name, Params, Value) { } had Value swapped over to 2nd, with Params* becoming the new variadic parameter. guess its that way currently for consistency's sake wrt the other meta functions:
Sometimes continuing to the next line is adequate, or just saves time during debugging (vs. editing and restarting the script to bypass a trivial error).
I experience an issue of not being able to exit the misbehaving script on a repeating continuable error "Target control not found". Try to continue anyway? I press No. It exits the thread then creates it anew and the same error pops up again and again... I'll add an example later.
Request: To add a third button (LOL) to exit all the threads and terminate the application.
43. Similar to #42, a button to unconditionally exit a parsing process (terminate AHK session) after the first warning dialogue at loading time. Quite often, the script writer just wants to stop repeated warnings and get back to editing.
44.
To stop a file that is currently playing, use SoundPlay on a nonexistent filename as in this example: SoundPlay "Nonexistent.avi".
And the whole routine of stopping the currently playing sound by starting to play again another non-existent sound file doesn't feel right at all.
Now that AHK discerns strings from pure numbers, it could be a pure number 0 (NULL, FALSE) as a soundplay first parameter to stop playing.
Also, I believe we might have another command to simply stop playing w/o throwing. soundstop() or similar. @swagfag proposed a custom function:
It would be great to have it on C++ AHK side eventually.
45. superseded by & operator in v2-128)
45. Having new load-time warning This var appears to never be assigned a value is absolutely amazing, but what about output variables like x,y,w,h in getpos(x,y,w,h)
One has to initialize them: getpos(x:=0,y:=0,w:=0,h:=0)
but these variables r used to retrieve values only and initialization is redundant here.
A preposition to add additional syntax keyword to declare output variables so as not to be checked for initialization/assignment.
thx to @iseahound, C# syntax looks pretty much relevant here: getpos(out x, out y, out w, out h)
My proposal is not to make out keyword mandatory, but to give a script writer 3 choices:
1. disable all warnings with #warn and have some output variables unassigned.
2. explicitly assign some values to them, e.g.: getpos(x:=0, y:=0)
3. do not assign any values but declare them as output variables: getpos(out x, out y)
Also it helps to memorize parameters' order. @swagfag: 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
46. Revolt! Revolt! the majority wants command syntax to be completely removed. Some ppl do like to use function calls w/o (). How about a compromise: #nocommands (#commands off, #syntax strict, whatever) pre-processor directive to disable command syntax (forcing to use () for function calls) in the script being parsed so that f (37,38) lines would finally trigger a warning or error.
Basically the major issue of command and function syntax co-existence is the general expectation that follows other languages (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.
wrong expectation example
I either bumped into something again or miss something...
g:=gui.new(), f:=g.add("edit"), g.show()
msgbox(f.type)
try f.type ;Error: the value of type "Gui.Edit" has no method named "type". OH, REALLY?
catch e
msgbox(e.message)
Error: the value of type "Gui.Edit" has no method named "type". Not expected:
1. it manages to msgbox(f.type) before failing try f.type
2. type is a property: https://lexikos.github.io/v2/docs/objects/GuiControl.htm#Type yet the error description complains about "a method".
So... if explicit expression syntax used by enclosing it in parenthesis, it works:
Also, var inside [explicit] expressions and outside them (after control flow statements, in the line beginning) processed differently and misleads even more:
DllCall could actually support both notations - regular and abbreviated at the same time for compatibility sake.
48. (bug) Some control flow statements don't allow a space between a command and its expression: try(f.type) ;syntax error vs if(g) ;OK
Another inconsistency with control flow statements: if (expr)=if(expr), but return (expr) is not the same as return(expr).
f:=1
if(f){ ;OK
}
if (f){ ;OK
}
e()
e(){
f:=1
return(f) ;call to non-existent function (unexpected). somewhat fixed in v2-128. no errors here
;return (f) ;OK
}
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.
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?
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 Any string value or variable. Numbers are not supported. -> Any string/numeric value or variable.
or the command trim|ltrim|rtrim to be changed to throw ("expected a number but got a string" or something like that).
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:
51. Encapsulate all built-in A_*** variables into super-global "ahk" object (such modularity might be good for the future language updates): A_Args, A_LineFile, A_AhkPath, etc -> ahk.Args, ahk.LineFile, ahk.AhkPath, etc. In case it doesn't hit the performance much.
-------------------------- update notes 1. Please, always specify full function/method/property names b/c it has to be searchable:
Merged ControlGet/SetTab into ControlGet/ChooseIndex.
->
Merged ControlGetTab/ControlSetTab into ControlGetIndex/ControlChooseIndex.
Changed Group(De)Activate/Close to throw
->
Changed GroupActivate/GroupDeactivate/Close to throw
2. Implemented #Requires is skipped for v2-113 notes.
3. I feel like people r reluctant to upgrade. I know, for the developers, its hard to keep in mind and cover all the stuff in the update notes, but a workaround/replacement for removed/restricted/changed features should be provided whenever possible. E.g.: "for //, //= old behavior use floor(a/b)"
Having instructions of what exactly has to be replaced with what exactly in order to upgrade from previous version(s) would greatly motivate people upgrading.
-------------------------- documentation
There r lots of typos, I lost some notes so I recall only a few for now:
SPI_GETMOUSESPEED := 0x70
SPI_SETMOUSESPEED := 0x71
F1::
; Retrieve the current speed so that it can be restored later:
DllCall("SystemParametersInfo", "UInt", SPI_GETMOUSESPEED, "UInt", 0, "Ptr*", OrigMouseSpeed := 0, "UInt", 0)
; Now set the mouse to the slower speed specified in the next-to-last parameter (the range is 1-20, 10 is default):
DllCall("SystemParametersInfo", "UInt", SPI_SETMOUSESPEED, "UInt", 0, "Ptr", 3, "UInt", 0)
KeyWait "F1" ; This prevents keyboard auto-repeat from doing the DllCall repeatedly.
return
F1 up::DllCall("SystemParametersInfo", "UInt", SPI_SETMOUSESPEED, "UInt", 0, "Ptr", OrigMouseSpeed, "UInt", 0) ; Restore the original speed.
;create a map with a customizable default value:
class m extends map {
__new(default := 0) => this.default := default
__item[key] => this.has(key) ? base[key] : this.default
}
8.https://lexikos.github.io/v2/docs/Functions.htm#Local
Some "pay attention" warning in the documentation concerning local, global, static commands in conjunction with chaining assignments. local a:=0, b:=0, c:=0 and local a:=b:=0, c:=0. The latter is not declaring all the 3 vars as local. Sometimes its easy to overlook...
11.obj.HasProp: returns true if the value has a property by this name, otherwise false https://lexikos.github.io/v2/docs/objects/Any.htm#HasProp obj.HasOwnProp: returns true if the object owns a property by this name, otherwise false. https://lexikos.github.io/v2/docs/objects/Object.htm#HasOwnProp
To add an explanation of the difference (by Helgef): HasOwnProp returns true if the object owns it, HasProp returns true if it or one of its bases owns it.
12.https://lexikos.github.io/v2/docs/Objects.htm#ObjPtr
The current object structure detailed explanation (offset/description) that might be very useful for ones trying to figure out how to organize an interface between compiled code and AHK objects.
etc. The format is changing and scripts using this "low-level" mechanics won't be compatible with other AHK versions but it won't be compatible with future alpha versions anyway... And AHK object's layout is changing not so fast, mainly via major updates only.
13.https://lexikos.github.io/v2/docs/objects/GuiOnEvent.htm
>Callback
>Type: String or Function Object
Needs to be replaced. No more strings accepted for function names.
14. fixed in v130
14. https://lexikos.github.io/v2/docs/Functions.htm
Quite comprehensive article but it actually lacks a subsection concerning functions declaration completely. And along with adding the v128 announcement statement (I consider it a crucial one):
Function and variable names are now placed in a shared namespace.
Each function definition creates a constant (read-only variable) within the current scope; therefore, scripts cannot assign to a variable with that name in the same scope.
something concerning "unlike functions, its possible to assign to a method with that name in the same class" should be added, too. Mentioning that some previous AHK v2 versions had separate property and method namespaces and thus some scripts might have conflicting property and method names that doesn't trigger an error in v128+ yet fails.
(hidden: stuff concerning v2-112 update)
Updated from v2-108 to v2-112. Just a bliss! Such a good trend and progression for AHK community! a) With removal of inner commands returning errors via ErrorLevel and replacing them with consistent and unified exception throwing mechanism we r further away from outdated batch paradigm of 1980ss. b)#If/if keywords r free now and can be used for some preprocessor macro system implementation in the future. c) Footprint of interpreter's .exe is smaller and scripts run faster. it was a depressive slowdown after v2-103 down to 2..25%. Now the performance of v2-112 is similar to v2-103 again. I guess some of these optimizations r due to replacing &string (address of) with a separate strptr() function, "on"|"off" literals with simple boolean numerals, and so on. the less redundant operations/check-ups in the main AHK cycle the better performance.
utils used throughout the topic
to collect and print out debugging info.
Generally a good list but here are some things I noticed after reading it once
7: What do you want to enumerate in the case of file?
8: the issue isn't g it's gui. There is no local variable named gui.
13: How would you output that data?
1. parser rewrites = ahk v3. probably
2, 3. i guess, more introspection is better
4. consequence of how strtol works, returns 0(black) if the conversion/parsing fails. making it throw would required extra parsing logic. or alternatively if gui option strings were to be removed(ie u set everything with properties exclusively .BackColor), u could make it accept integers and strings only. strtol wont be needed, neither would extra parsing
5. i guess
6. its hardcoded in source that prototype object cant be passed to for loops(throw automatically). what exactly do u expect a XXX.Prototype's enumerator to enumerate anyway?
7. such as?
8. no, it wants u to explicitly import the super-global Gui in the forced-local function scope
9. sounds very niche with all these additional stipulations, but i guess
10. more reasons to delete the command syntax. heres another one
11. check the docs changes, check the source changes, check the alpha updates thread, check v2-changes(which should be tracked somewhere btw hint hint)
12. no comment
13. i guess
14. indifferent
15. no comment
vvhitevvizard wrote: I guess some of these optimizations r due to ...
More likely, the compiler has opaquely changed its optimization decisions. The changes to &var and On/Off should have zero effect on parts of the script that didn't use them in the first place.
for k,v in dbg2.prototype ;error: Type Mismatch
Map's __Enum method enumerates key-value pairs. It requires a Map. You gave it a Prototype, so __Enum threw a type mismatch exception. If you don't know what it is that you're enumerating, you should not be enumerating. If you want properties, use the OwnProps enumerator.
File and some other built-in objects should expose more properties for an enumeration.
All properties of all objects (except COM objects) are enumerable, but you must use OwnProps on the object that actually defines those properties. In future there might be an enumerator which builds a list of all inherited properties and enumerates those. In any case, there's no difference between enumerating properties of built-in objects vs. properties of user-defined objects.
msgbox("text"), abort ;???. it ignores the right side of the line
It isn't ignored; it just has no effect, like in x := (abort, 1). There's no "UseUnset" warning because the variable abort is not being used. This may be "fixed" when I change how unset variables are detected. (It is by design that some references do not cause a warning, such as for IsSet(abort).)
swagfag wrote:10. more reasons to delete the command syntax.
MsgBox(), abort not being detected as an error has nothing to do with function call statements. It simply is not an error. In this case I think vvhitevvizard was not trying to use a function call statement, and the existence of that feature did not inhibit error detection.
vvhitevvizard wrote:11. for k in obj acts different in v2-100 and v2-108. the former returns an index, the latter returns a key.
What kind of object is obj? In both versions, k depends on this. In v2-a100, in most cases it was a key (not index). In v2-a108, it is generally a key or array element.
v2-changes wrote:Since array elements, properties and methods are now separate, enumerating properties and methods requires explicitly creating an enumerator by calling OwnMethods or OwnProps.
...
For-loop usage [for Array] is for val in arr or for idx, val in arr, where idx = A_Index by default. That is, elements lacking a value are still enumerated, and the index is not returned if only one variable is passed.
There is more detail in the documentation, under the __Enum method for whichever type of object you are enumerating.
vvhitevvizard wrote:There is no mention of removed .Count() for objects as well.
Under Map (in v2-changes) there is "Count is now a property." There is also "ObjCount was replaced with ObjOwnPropCount (a function only, for all Objects) and Map has its own Count property." ObjOwnPropCount(myMap) and myMap.Count and myArray.Length are counting different things.
varsetstrcapacity: 4 words within 1 keyword, why so long? Why not SetStrCapacity, StrCapacity or something?
1. Each word has meaning - it makes the purpose of the function clearer. It operates on a variable, not a string, but sets its capacity to hold strings.
2. It purposely discourages you from using the function. If you use it, you should feel the weight of its long name.
Four words... three of them are only three letters each, and you think that's long. What about UrlDownloadToFile or WinMenuSelectItem? FileCreateShortcut is fewer words but still longer.
15a. ControlGetList article is absent now:
That's because it is being renamed to ControlGetItems in the next update.
swagfag wrote:6. its hardcoded in source that prototype object cant be passed to for loops(throw automatically).
That's news to me.
Prototype objects cannot be passed (as this) to native methods belonging to specialized types, because Prototype objects are just a collection of properties and methods (natively just an Object). Taking a step back from the implementation, Map.Prototype is not an instance of Map; it has not been constructed, so calling instance methods on it is not valid.
However, any object or value can be passed to for if it has an __Enum or Call method that can be called. It doesn't need to have one by default - you can define one.
I've just updated the first post - changes start from paragraph 13, added more bugs and suggestions, and merged my 2 previous topics. So to have all the stuff in one place.
@Helgef Maybe its better to introduce some new #warn mode then? Check for # of recursions. E.g. 256 nested calls -> throw a warning. Or the opposite, a switch allowing it. With error signalling by default.
And my example is a tad different - we call outer function inside nested function/closure. And both have the same name... there might be a check at loading time to warn about this.
@nnnik
7: What do you want to enumerate in the case of file?
Self-logging script. File.pos, etc. One could just do a general for-in loop thru classes/global vars to save the current script's state. File object might be not the best example. But exposing as many properties as possible is not a bad thing for built-in objects.
8: the issue isn't g it's gui. There is no local variable named gui.
but gui.new should act as array.new. gui should be a super-global class name, right?
13: How would you output that data?
I added an example in the 1st post. Pls have a look.
We r in an eternal alpha state, there is no backward compatibility reasoning! And I thought "expression is too long" issue leads to some constant limiting it artificially.
3... or alternatively if gui option strings were to be removed(ie u set everything with properties exclusively .BackColor), u could make it accept integers and strings only. strtol wont be needed, neither would extra parsing
this! instead of going thru combining/parsing string of arguments like this: "s16 Bold Center vcenter c" var1 " "x " var2 " y15"
we could exploit gui/gui control class features adding
Further away from AHK1ish quirks and closer to uniformity.
6. what exactly do u expect a XXX.Prototype's enumerator to enumerate anyway?
I was trying to enumerate my objects to store current script's state as JSON output to a file. And It throws when I tried to enumerate some of my objects due to them extending Map class.
If u r curious, here is the code:
;v1.7.1 | AHK v2-a112
;26-06-2020
class json{ ;REQ: log, num, mcode, str
;1) 0=enable indentation and line feed, 1=the most compact representation:
;2) spaces per level, similar to JavaScript's JSON.stringify() 'space' parameter
; JSON array elements and object members will be pretty-printed with the indent level
static Compact:=0, Space:=4
, Opt:=0 ;[1|2] bits: 1=throw "not an object", 2=add "a_type" pseudo-property for any special object
;"stringify": obj to JSON str: str:=json.Dump(obj). returns empty-line if non-object passed
static Dump(_o, _d:=""){ ;_o:obj, _d:cur indent
static q:='"', p:=0, t
o:=this
if(!isobject(_o)){ ;"string" | number
(o.Opt&1) && o._E("Not an object.")
return
}
(p=o.Space) || t:=Tab(p:=o.Space)
, (o.Compact) ? n:=d:="" : (n:="`n", d:=_d t), varsetstrcapacity(s,1024*8)
y:=Type(_o), y=="Array" ? a:=0 : y=="Object" ? a:=1:a:=2
(a>1 && o.Opt&2) && s.=d q "a_type" q ":" q y q "," n
for k,v in (_o.hasmethod("__Enum") ? _o:_o.ownprops())
s.=d (a ? q k q ":":"") (isobject(v) ? o.Dump(v, d) ;recursive
;escape backslash and quote, format float numbers
: ((b:=Type(v))=="String") ? q v q : (b=="Float") ? num.T(v) : v) "," n
;=Format("{:g}",v)
return (a ? "{":"[") (s ? (n rtrim(s, "," n) n _d) : "") (a ? "}":"]") ;wrap in brackets
Tab(n)=>--n ? Tab(n) " ":"" ;expand tab to spaces
}
}
so its used like json.dump(obj_name) to have pretty-printed/compressed output of an object. its good to debug objects ad hoc as well
8. no, it wants u to explicitly import the super-global Gui in the forced-local function scope
Oh, I see now! Anyways, I used to pre-face a function body with listing of all the locals - just a habit and it helps tracing which 1-letter names r not taken yet (I prefer local vars to be tersely named):
9. sounds very niche with all these additional stipulations, but i guess
Consider it as basic elements to speed up routine cycles. Ok, drop aside trimming (its up to a programmer to feed dirty input, it can be filtered with regex). But the core conversion has to be as fast as possible. Adding just a KB to the .exe by implementing map.fromstr(), map.tostr() or something, we boost performance for such [niche] functionality. of course this logic can be done via AHK script loop. But good performing list/tuple/range/dictionary/other is one of the key features that made Python so popular as a scripting language, I guess. And AHKv2 possesses greater possibilities as well, being a state of art scripting language.
10. more reasons to delete the command syntax.
yuppers! And there r not so many commands left to be converted
11... check the alpha updates thread, check v2-changes(which should be tracked somewhere btw hint hint)
yeah I follow those files and even keep my own version of all the update notes, those v2-changes, lexikos' posts combined, and with added heavy highlighting to keep track what keywords got removed/renamed
Clipboard01.png (67.71 KiB) Viewed 11909 times
Updating scripts to new alpha versions is not a big deal if all the changes r mentioned.
13. i guess
I added an example of such an extended command usage, and added more points and some details to old ones. please have a look at 1st post.
The changes to &var and On/Off should have zero effect on parts of the script that didn't use them in the first place.
No checks for &preceding next keyword in the main cycle?
6... It requires a Map. You gave it a Prototype, so __Enum threw a type mismatch exception. If you don't know what it is that you're enumerating, you should not be enumerating.
The point is I just used a general recursive cycling thru all the exposed properties/nested objects of an object derived from Map. And I started from the surface.
for k,v in (_o.hasmethod("__Enum") ? _o:_o.ownprops())
Function logic here is if something of built-in matter has __Enum then it can be enumerated via regular for k,v in obj and it should expose something w/o throwing. Otherwise we fall back to browsing thru obj's properties via for k,v in obj.ownprops().
Pls look at the full code in the post above this to get an idea why would one need to get there.
10... It isn't ignored; it just has no effect, like in x := (abort, 1).
I see. Well, all that stems from mixing command syntax with function syntax... Until we eradicate commands one day.
11... There is more detail in the documentation [skip]
Thank u for providing links... It renders point 11 as irrelevant.
Last edited by vvhitevvizard on 28 Jun 2020, 10:56, edited 3 times in total.
Execution routine after gc.onevent goes to F1() (nested function) and then we call ourselves, not an outer one. u r right I updated 1st post.
But the point is the stuff silently crashes breaking even thru the scripts's critical error terminating route, not reaching catch/onerror/onexit control points. I would like to have a #warn setting mode to do some sanity check at loading time for presence of recursive calls to warn. In case a programmer let it be by mistake. Just in case. Recursion is a type of programming art not just anyone can master w/o dramatic performance drop and thus it rarely used in actual practice.
Or any changes to the interpreter to make it able to intercept at runtime long _before_ it crashed, devoured the whole memory and dealt other ruining.
If a script can ruin an interpreter w/o explicitly fiddling with process of the latter via system calls/external tools then its the interpreter fault after all.
Last edited by vvhitevvizard on 28 Jun 2020, 11:57, edited 2 times in total.
It purposely discourages you from using the function. If you use it, you should feel the weight of its long name.
I knew u would mention it! Its use is very rare now indeed - mostly I see only 1 usage case of setting capacity big enuf to accommodate many strings concatenations w/o resorting to malloc().
14... Each word has meaning - it makes the purpose of the function clearer.
But common sense of AHK Creator should insist on making keywords as short as possible. Some long-tail keywords r too cumbersome to be easy-to-handle. Don't u ever reflected on having an "online" command line interface an interpreter should have? To type script's code line by line and immediately see the reaction. Great for small formulas.
If u wend this route of "every sub-word in a keyword is meaningful" then nothing would stop u from creating more and more prefixes and suffixes to keywords as time passes by... StrCapacity() is the compromise IMHO - it has a return value so Set suffix is kinda confusing here. And we tackle a string variable, not a numeric one - so Var is redundant. IMHO. Its 11 letters still to be quite discouraging!
Pls check it I updated the 1st post adding more reasoning from an AHK user point of view. Up to point 23 now.
15. I will probably leave BufferAlloc as is or replace it with Buffer.Alloc(). I originally named it BufferAlloc and not BufferCreate or BufferNew because I anticipated other ways to construct a Buffer object. And when it comes to low level stuff like this, I think the added clarity is important.
I disagree with the suggestion to use File.New(). FileOpen opens a file for reading or writing, and returns an object that can be used to read or write. It does not make a new file. I'll probably rename the class to something like FileHandle, FileIO, FileStream, IOStream, etc. Internally there's a C++ class named TextStream that does most of the work, but we're not always dealing with text.
... and it should expose something w/o throwing.
What do you think a Prototype is for? 1) It is not intended to be used directly, except to define things for instances. 2) It cannot define methods for itself, because all methods are intended for instances of the class. Your approach is incorrect; you must give special handling to Prototype objects (or avoid touching them), regardless of whether they actually throw an exception.
But the point is the stuff silently crashes breaking even thru the scripts's critical error terminating route, not reaching catch/onerror/onexit control points.
If the program has run out of stack space, it is unable to handle any script-level exceptions or execute script, and must terminate. It's up to the OS and system settings whether you get an error message; generally the error is logged (see Event Viewer, Application log). See Show error or debug dialog on Call Stack overflow - Suggestions.
The point is I just used a general recursive cycling thru all the exposed properties/nested objects of an object derived from Map.
You don't know the meaning of the values returned by __Enum by some arbitrary object, but it's likely that they aren't properties. I've used the convention that __Enum returns a key or index corresponding to __Item, but other objects can do whatever they want. If you want your script to be fault-tolerant while using unknown methods, you need to use exception handling. And in general, don't call anything on a Prototype.
No checks for & preceding next keyword in the main cycle?
The expression is parsed when the script is loaded, not during "the main cycle".
But common sense of AHK Creator should insist on making keywords as short as possible.
This is your opinion, and I obviously disagree.
And we tackle a string variable, not a numeric one - so Var is redundant.
That makes no sense. "Var" is to distinguish between variables and values, not strings and numbers. StrPtr works with string values. VarSetStrCapacity works only with variables. The variable does not have to contain a string before you call it, but it will after.
Well, all that stems from mixing command syntax with function syntax...
I already explained that it does not. Or is it because you were attempting to use a function call statement? If so, that's your choice. But given that you "accidentally inserted a command from NSIS", I assume you weren't actually cognizant of what is valid in AutoHotkey anyway, so whether or not we support function call statements is irrelevant.
It would be no more dangerous at worst, and safer at best, than the alternative - using Ptr directly, without the bound checking granted by Size. It's not about reusing memory, but accessing data from some source that does not create a Buffer (probably a DllCall). If you are the one allocating the memory, you can just use BufferAlloc and reuse the Buffer.
Would it be possible to have a default for .member syntax
No. That's reserved for line continuation.
I think your "documentation issues" #3 and #5 were fixed before you posted about them.
I think your "documentation issues" #3 and #5 were fixed before you posted about them.
Great I noted above I just merged my 2 old topics into this one. Just to to have my thoughts "centralized". Previous were 2+ months old.
Now I start hiding resolved/irrelevant points under spoilers.
BTW, what do u think of point 12? In general it needs some vars declaration ability within an expression of fat-arrow. To be able to inline local vars usage. Right now I cannot use local variables inside fat-arrow function that declared at global scope. And all that mess is due to fat-arrows at outer scope r assume-global...
21... No. That's reserved for line continuation.
Currently, its vacant for new syntax rules: concatenation/line continuation requires spaces between. operator:
msgbox("aa"."bb") ;error
msgbox("aa" ."bb") ;error
msgbox("aa". "bb") ;error
a:="aa"
."bb" ;error
msgbox(a)
a:="aa", b:="bb"
msgbox(b .a) ;error. what about this one to have another meaning inside methods -> msgbox(b this.a) ?
a:="aa", b:="bb"
c:=a
.b ;error. and same for here
msgbox(c)
-> Error: Illegal character in expression
15... I disagree with the suggestion to use File.New(). FileOpen opens a file for reading or writing, and returns an object that can be used to read or write. It does not make a new file.
But it does create an AHK built-in object for further operations via its methods. Even tho we create a new file handle just for reading w/o creating anything new at file system level: new relates here to creating a new AHK object.
17... If you are the one allocating the memory, you can just use BufferAlloc and reuse the Buffer.
Let's imagine,we've got a huge memory region created not by AHK's bufferalloc/File.RawRead or others. It might be an aftereffect of some external library call. We do have a pointer and we know a current size of that region and we want to use Buffer object facilities to control its boundaries and to tackle it using NumPut or other Buffer-aware functions. And we don't wanna waste CPU and memory resources by copying that memory region. b:=BufferFrom(addr, size) and voila! We've just reduced our carbon dioxide emission level!
6... Your approach is incorrect; you must give special handling to Prototype objects (or avoid touching them), regardless of whether they actually throw an exception.
But additional checkup or additional try/catch block would slowdown the inner loop. And the whole function loop might be very resource-intensive, I remind u - some of my output JSON files r 10+MB in size. Is it possible to just remove __Enum property for items that r not supposed to be for-in looped?
You don't know the meaning of the values returned by __Enum by some arbitrary object, but it's likely that they aren't properties. I've used the convention that __Enum returns a key or index corresponding to __Item, but other objects can do whatever they want. If you want your script to be fault-tolerant while using unknown methods, you need to use exception handling. And in general, don't call anything on a Prototype.
Actually for the function logic it doesn't matter what data type for-in loop returns. It handles any valid AHK entity (numeric/string var, object, empty /zero length ones) w/o any errors. The exception happens inside for k,v in obj.
16... If the program has run out of stack space, it is unable to handle any script-level exceptions or execute script, and must terminate. It's up to the OS and system settings whether you get an error message
Thank u for sharing that link. But I meant an inability for a script that ran autonomously for a period of time to terminate itself in a regular way to save some critical data etc. And there is no way to control RSP (stack pointer) inside AHK scripts... It might be possible to implement some checkups inside CPP code. Im not sure how to do that for ur VisualCPP build but for GCC it goes like this:
Anyways, what do u think of having a warning about presence of any recursive calling enabled by default and requirement of using some #warn mode disabling syntax to let it be in the script? For the reason its potentially dangerous. And most time any recursive calling is left unintentionally by function misnaming.
Last edited by vvhitevvizard on 29 Jun 2020, 11:35, edited 4 times in total.
It would be no more dangerous at worst, and safer at best, than the alternative - using Ptr directly, without the bound checking granted by Size.
Its dangerous in that sense it creates the false impression of safety having boundary checking while using a random memory region.
The expression is parsed when the script is loaded, not during "the main cycle".
Roger that!
This is your opinion, and I obviously disagree.
I just suggest. Sometimes plainly brainstorming. Naming is not of top-importance, but some of my points are, I guess. Especially one concerning refactoring of preprocessor - google "ahk preprocessor" - it might be one of the most desired features to add.
Your task is to weigh suggestions and decide whatever balanced changes to be done. So far so good - U've done great job - AHK v2 feels state-of-the-art. But its never-ending progress.
"Var" is to distinguish between variables and values, not strings and numbers. StrPtr works with string values. VarSetStrCapacity works only with variables. The variable does not have to contain a string before you call it, but it will after.
ok. StrPtr is from a binary formats family and VarSetStrCapacity belongs to a variables family.
I just don't like longtail keywords... BTW what do u think of having "String class" overloading with "".len() to exist in the language by default?
Or is it because you were attempting to use a function call statement? If so, that's your choice. But given that you "accidentally inserted a command from NSIS", I assume you weren't actually cognizant of what is valid in AutoHotkey anyway, so whether or not we support function call statements is irrelevant.
From a user point of view: indignation here b/c of I inserted a random literal name after closing function's arguments list parenthesis and AHK ignored it.
I guess AHK logic here its a concatenation of msgbox("here") return value and undeclared var's value that is equal to "".
It might be on purpose for some cases but leftovers that r usually plain typos left by a script programmer r going unhandled.
Its expected to throw some warning for non-declared var usage in an expression/function call w/o any function definition somewhere else. As it happens regularly in recent AHK v2 versions.
Also, the result of the expression that has no assignment, not used as a parameter for a function call, and is not used by return, should have a respective #warn mode...
And overall I hate all that command syntax. I avoid uing any commands except for plain loop/else/break/throw.
If I used a random memory region, I would not have an impression of safety, false or otherwise.
I guess AHK logic here its a concatenation
You guess wrong. If you removed the comma, it would be concatenation. With the comma, it's a multi-statement expression.
If you insert random text into a script file, there is a non-zero chance that it will be misinterpreted as something legitimate. That is not a problem of the language or the program; it is your problem.
As for referencing an uninitialized variable in this context, it will raise a warning by default in the next release, even though its value is not being used.
There will be no warning for merely lacking a declaration (because an undeclared variable can be assigned a value). Variable declarations are intentionally optional.
Language - Expression Statements wrote:Expressions that start with any of those described above (but not those described below) are also allowed, for simplicity. For example, MyFunc()+1 is currently allowed, although the +1 has no effect and its result is discarded. Such expressions might become invalid in the future due to enhanced error-checking.
And overall I hate all that command syntax.
You are free to hate and blame, but it changes nothing. Again, command syntax has nothing to do with MsgBox(), somevar.
Right now concatenation/line continuation requires spaces between dot operator so place for new syntax is not taken yet
Stop editing replies into your first post.
Anyway, you are mistaken yet again. a ."b" is obviously invalid, because valid identifiers cannot contain quote marks. a .b is obviously invalid if a has no b property. Strings have no b property by default.
12. Fat arrow functions are intended for passing to other functions inline, and generally will be referring to variables from the outer scope. If you need to create local variables, use a proper function, or define the fat arrow function inside another function. How many functions must you define in global scope, that you need to cram them onto one line?
23. You can do whatever you want by overloading the __Item property.
But it does create an AHK built-in object for further operations via its methods.
So do FileRead (with the RAW parameter) and other functions, for other types of objects. Maybe there will be other functions that return an IO object.
Let's imagine
I live in the real world (for some of the time), and can't recall ever encountering a need for what you describe, in AutoHotkey.
But additional checkup or additional try/catch block would slowdown the inner loop.
But incorrect code will make your program not work. Is it better to fail quickly?
Is it possible to just remove __Enum property for items that r not supposed to be for-in looped?
I think you're missing the point of a Prototype. If you remove a method from the Prototype, objects derived from it will no longer have that method...
But I meant
I have no idea what you're talking about.
Anyways, what do u think of having a warning about presence of any recursive calling enabled by default
Like Helgef said, no.
v1.1.33 and v2.0-a113 will catch the stack overflow exception and display a message. By that point, the entire call stack will have unwound, so there will be no chance of recovery.
As for referencing an uninitialized variable in this context, it will raise a warning by default in the next release, even though its value is not being used.
There will be no warning for merely lacking a declaration (because an undeclared variable can be assigned a value). Variable declarations are intentionally optional.
Assignment w/o declaration is ok for a scripting language of loosely typed simplified system, right side operand (string, integer, float, object) defines its type. But using a value of non-declared and non-assigned variable should be an error!
10... You guess wrong. If you removed the comma...
Oh dang I forgot about the comma. I'm cursory here and there. I hope people to not split hairs, I pleaded for the issue of garbage text used w.o any errors to be addressed.
That's the idea - to remove some wrong conclusions of mine and distracting stuff and add details to outline the essence of the wish list.
And I'm desperate - some stuff I tried to bump up seems to be very daunting for the AHK developers team so despite of multiple [similar issue] requests year by year it seems to be never released.
Last edited by vvhitevvizard on 30 Jun 2020, 08:56, edited 2 times in total.