Understanding AutoHotkey

Helpful script writing tricks and HowTo's
func
Posts: 36
Joined: 20 May 2016, 20:52

Understanding AutoHotkey

21 May 2016, 18:25

My theory is that the main difference between truly masterful AutoHotkey scripters (of which there are only a few) and casual and intermediate scripters is the fluent understanding of the entire range of useful AutoHotkey syntax and vocabulary.

Programming/conversation design patterns and efficient structures all evolve quickly and naturally once you fully understand any language.

I derive this insight from how I learned and applied my most fluent language, English, which I use every day.
In order to write, read, and formulate meaningful English conversations I rely on a constantly accessible and vast database of English syntax and English vocabulary. Spellcheck is my only English IDE feature; I don't need to know how to spell all words I merely need to know that useful words exist, how to approximately construct such words, and where to use such words.

All higher level English design patterns, such as writing a novel, a short how-to guide, formulating an interview questionnaire, writing a blog post, writing a book review, etc. come naturally by seeing first how others use such patterns, and then making my own unique instances of these patterns using the same building blocks - the English language syntax and vocabulary. My theory is that AutoHotkey (and any other language) patterns can be instantiated much the same way.

In order to become fluent in AutoHotkey and create useful and advanced AutoHotkey conversations with computers I need to be able to have a deep understanding of the syntax and vocabulary of AutoHotkey, so much so that there are basically no major "what-ifs" or "vocabulary gaps" during coding that stop my flow of attention. After a coherent plan has been formulated, much like a coherent plan about what you want to say when writing a book or movie review, there should be almost no writers block when writing, but merely quickly refactoring/restructuring as I go along to optimize the conversation, only pausing to read/reread some code already written, just like when using English fluently.

Even when other people write poorly formatted or structured English, I can always decipher exactly what each word is, even if I don't derive the contextual meaning. Reading AutoHotkey code I cannot yet do this, even reading the documentation I cannot yet do this.
Therefore I will begin by systematically exploring the AutoHotkey syntax and vocabulary using commented tests written in AutoHotkey. It will be a slow process spanning over one or two months. I suspect that I will have to update, extend, restructure, and refactor the comments as well as the test code many times over this period.

Here is what I started with today. My systematic process is to copy pages or partial pages from the documentation into SciTE4AutoHotkey, break each concept down into its elemental parts, and tested each elemental part in order to link each concept in the documentation with a behavior I will come to expect. The collection of all of these concepts linked to all of these behaviors will result in an ever growing understanding of AutoHotkey.

I highlight each snippet in SciTE4AutoHotkey, right click, and "run selection" to test each block as I'm writing them.

Taken partially from: https://autohotkey.com/docs/Variables.htm

Code: Select all

;~ Variable types: AutoHotkey has no explicitly defined variable types. However, a variable containing only digits (with an optional decimal point) is automatically interpreted as a number when a math operation or comparison requires it. (To improve performance, numbers are cached internally to avoid conversions to/from strings.)
a := 5 ; the variable "a" contains only a digit, but is not interpreted to be a number yet
b := a + 5 ; the variable "a" is required for a math operation (a + 5) so it is interpreted to be a number type variable because it's used in a math operation on this line
msgbox % "b is equal to: " . b . "`nThis is because the variable ""a"" was interpreted as a number, and added to 5 (b := a + 5) to evaluate to 10."
if a = 5 ; the variable "a" is required for a comparison (if a = 5) on this line, since the variable "a" contains only digits, it is interpreted as a number
{
	msgbox % "yes, the variable ""a"" is equal to 5!"
}
else
{
	msgbox % "no, the variable ""a"" is not equal to 5!"
}

; Here is an example where a variable does not contain only digits, so it's not interpreted as a number type variable when used in math operations or comparisons.
a := 5grams ; the variable "a" contains contains a digit, but also non-digits : it is not interpreted yet
b := a + 5 ; the variable "a" is required for a math operation (a + 5) but "a" does not contain only digits, so it's not interpreted as a number, the math operation fails silently and "b" evaluates to nothing (empty)
if a = 5 ; the variable "a" is required for a comparison (if a = 5) on this line, since the variable "a" contains only digits, it is interpreted as a number
{
	msgbox % "yes, the variable ""a"" is equal to 5!"
}
else
{
	msgbox % "no, the variable ""a"" is not equal to 5!"
}


;~ Variable scope and declarations: With the exception of local variables in functions, all variables are global; that is, their contents may be read or altered by any part of the script. 
a := "test"
gosub MyLabel
return
MyLabel:
msgbox % a ; the variable "a" is found because variables created outside functions are accessible everywhere in the script except inside functions.
return

;~ To access variables created outside functions and use them inside functions they must be declared global variables outside of functions.
global a := "test"
msgbox % myFunction()
myFunction() {
	b := a . " worked"
	return b
}

;~ Alternatively, functions themselves can be declared global so that they can use variables created outside of functions.
a := "test"
msgbox % myFunction()
myFunction() {
	global ; this function is declared a global function by placing "global" on the first line inside of the function
	b := a . " worked"
	return b
}

;~ Except where noted on the functions page, variables do not need to be declared;


;~ Variables come into existence simply by using them (and each variable starts off empty/blank).
if (a = "") ; the variable "a" comes into existence simply by using it in a comparison
if b = ; the variable "b" comes into existence simply by using it in a comparison
c := ; the variable "c" comes into existence simply by using it when you assign it (even assign it to contain nothing as in this example)
msgbox % "a: " . a . "`nb: " . b . "`nc: " . c . "`nThe variables came into existence, but their contents starts off empty/blank."


;~ Variable names: Variable names are not case sensitive (for example, CurrentDate is the same as currentdate).
CurrentDate := "December 5th, 1805"
currentdate := "January 6th, 1906" ; The currentdate is the same as CurrentDate, so CurrentDate/currentdate both contain "January 6th, 1906" on the second line.
msgbox % "currentdate: " . currentdate . "`nCurrentDate: " . CurrentDate . "`ncURRENTdATE: " . cURRENTdATE

;~ Variable names may be up to 253 characters long and may consist of letters, numbers and the following punctuation: # _ @ $
abc := "test 1"
123 := "test 2"
#a# := "test 3"
_b_ := "test 4"
@c@ := "test 5"
$d$ := "test 6"
msgbox abc: %abc% `n123: %123% `n#a#: %#a#% `n_b_: %_b_% `n@c@: %@c@% `n$d$: %$d$%

jesushh
Posts: 8
Joined: 09 May 2016, 04:59
Location: Malaga / Krakow

Re: Understanding AutoHotkey

30 May 2016, 19:04

Interesting post.
I like this stuff, how we learn and how we can improve this process.

From my (short) experience I would summarize that instead of theory, or using intelligence for guessing things....just do, do, do, try , try, try code.
And you expressed in a very comprehensible way, just trying many possibilities for every single concept in the docs.
By doing so, I guess the programmer gain an understanding of every command in a very "complete" way, instead of if you just use what is in the docs and it seems "it works".

I agree with the consideration that if you don't know part of the syntax/elements of the language it will be impossible to build scripts of certain level of mastery. This is obvious to some extent, though, if objects for example provide a much better way to do whatever you are doing; but you don't know them, of course you don't "understand Autohotkey" enough to use them. Maybe I am misunderstanding this point of yours :)

Thank you!
User avatar
evilC
Posts: 4358
Joined: 27 Feb 2014, 12:30

Re: Understanding AutoHotkey

09 Jun 2016, 14:55

Some insightful words there.

AHK is quite a special case, insofar as it has two syntax styles to wrap your head around and a set of rules that govern which of the two apply.

Consider the following code:

Code: Select all

#SingleInstance,Force

var := "hello"
a := "Hello"

if a = var		; False. Not expression syntax. Comparing variable a with "var"
	msgbox % "a = var is true"

if (a == var)	; False. Expression syntax, but case sensitive compare
	msgbox % "(a == var) is true"

if (a = var)		; True. Expression syntax, case INsensitive compare
	msgbox % "(a = var) is true"
Personally, I always try to use Expression Syntax and learn how to force expression syntax for any given command or parameter.
So you picked the absolute best page to start on IMHO. Understand this aspect of AHK and you will be well set up to get the most out of it.
victorel
Posts: 21
Joined: 09 Jun 2016, 14:44
Location: Canada

Re: Understanding AutoHotkey

10 Jun 2016, 12:55

I think equality comparison, by default, should mean "identical", and there should be a single symbol for that (either = or ==).
In case of strings, then case sensitive comparison. If the user needs case insensitive, then there should be a parameter or something to be added for this.

Then there will be less confusion.
User avatar
tidbit
Posts: 1094
Joined: 29 Sep 2013, 17:15
Location: USA

Re: Understanding AutoHotkey

10 Jun 2016, 13:37

I have never seen any confusion over Case when comparing. Making it more strict by default would probably cause a problem where there is none

If the user needs case sensitive, then there should be a parameter or something to be added for this. (oh wait, there already is. == does that :P one = for simple match (it's simple to type and read, it has simple rules) and == for fancy match (it looks fancy and different, fanciness indicates more strict rules to follow))

Also, great first and only post, func.
rawr. fear me.
*poke*
Is it December 21, 2012 yet?
victorel
Posts: 21
Joined: 09 Jun 2016, 14:44
Location: Canada

Re: Understanding AutoHotkey

10 Jun 2016, 17:13

But you agree it's a quirk :)
User avatar
tidbit
Posts: 1094
Joined: 29 Sep 2013, 17:15
Location: USA

Re: Understanding AutoHotkey

10 Jun 2016, 17:48

afaik, no language I have used has "case sensitive first". though, I've only used maybe 5.
it's usually always = to 'compare' and == or === to 'case sensitive compare'. I don't see it as a quirk.
rawr. fear me.
*poke*
Is it December 21, 2012 yet?
victorel
Posts: 21
Joined: 09 Jun 2016, 14:44
Location: Canada

Re: Understanding AutoHotkey

11 Jun 2016, 08:40

Really?
I've mostly used Matlab in the past, and there it's just "==" for all comparisons (and means case sensitive). Besides, they have string specific functions like strcmp() and strcmpi(), for case sensitive, and case insensitive.

In Python, there a single equality operator: "==" , and means case sensitive. For case insensitive comparison, one has to transform the strings first : http://stackoverflow.com/questions/3194 ... -in-python

C++, similar to python, http://stackoverflow.com/questions/1163 ... rison-in-c

Javascript, == for general equality, including string sensitive comparison http://www.w3schools.com/js/js_comparisons.asp , http://www.w3schools.com/js/tryit.asp?f ... omparison1

And here's a wikipedia list of string equality operators: https://en.wikipedia.org/wiki/Compariso ... 9#Equality . Languages often use a second operator to include type comparison as well, not for case sensitivity.

From above, my conclusion is that the common / normal in languages is that the general equality operator (be it = or ==, depending on language) applies to strings as case Sensitive comparison. And that makes sense, as Equality means equality in value (not approximately equal, or similar or equivalent etc). While to get case insensitivity, languages would use either other means like functions, or a different operator.

And in Autohotkey, it's the other way around. :wtf:

Maybe this should be in the Wish List or Autohotkey v2 / future forum?
User avatar
Xtra
Posts: 1269
Joined: 02 Oct 2015, 12:15

Re: Understanding AutoHotkey

11 Jun 2016, 14:23

There is nothing stopping you from using == for everything is there?
There is nothing in the docs saying one is the default and one is an option.I don't see the issue here.
victorel
Posts: 21
Joined: 09 Jun 2016, 14:44
Location: Canada

Re: Understanding AutoHotkey

11 Jun 2016, 20:39

True, nothing stops me from using my favorite symbol, and you from using yours. And same things goes with a number of other things syntax-related in Autohotkey.
However, this is cumulative, and what that leads to overall, is:
1. Harder to learn the language for beginners, both because it is nonstandard compared to most other popular languages, and simply because it's, well, more to memorize.
2. Harder to understand code written by other people.

By the way, similar suggestions regarding = and == were expressed by this person here https://autohotkey.com/boards/viewtopic.php?f=13&t=6969 , who is way-way-way more computer science literate than I am. Also, general stuff regarding familiarity, consistency, elegance of any programming language.
User avatar
Xtra
Posts: 1269
Joined: 02 Oct 2015, 12:15

Re: Understanding AutoHotkey

12 Jun 2016, 09:39

I don't have a favorite symbol i just use what's appropriate for what i'm trying to do.

1. The only thing making it difficult for people to learn AHK is all the old commands and backwards compatibility left in to not break older scripts.
AHK is one of the easiest languages to learn, definitely very forgiving and you don't need to memorize every little thing there is the help file for that.(Same goes for all languages)

2. If you write code sloppy and without comments etc of course that's going to make it harder for anyone to understand it.

Every language has differences in syntax. aka quirks...Nothing new here.
victorel
Posts: 21
Joined: 09 Jun 2016, 14:44
Location: Canada

Re: Understanding AutoHotkey

13 Jun 2016, 09:36

Well, I did not mean to say that Autohotkey is a hard language, but that it could be made easier to learn for the beginner by removing redundancies and quirks.
2. If you write code sloppy and without comments etc of course that's going to make it harder for anyone to understand it.
I think you misunderstood my message there. From the beginner's perspective, another thing that makes it harder is that he has to learn several ways of doing things (like several syntaxes for assignment, equality testing, etc, and both commands AND functions syntax) not because he cannot use just one of them, but because when he read's other people's code, he encounters them there. Plus, even the tutorials he is learning from are not sticking to strictly one variant of syntax available.

Of course, the more advanced users may not have any trouble with this, as they already know most variants.
Every language has differences in syntax. aka quirks...Nothing new here.
Sure, about every other language has its own quirks and faults, but I did not think that's an excuse not to try to get better.
So it remains up to developers to decide whether they want to pay more attention to newbies' wishes.
User avatar
lmstearn
Posts: 158
Joined: 11 Aug 2016, 02:32
GitHub: lmstearn

Re: Understanding AutoHotkey

13 Aug 2016, 10:09

Here's Func's code without the spaces. Just renamed a function, put the label at the end, the not existing vars at the start, and ran the whole lot from NPP. :bravo:

Code: Select all

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
; #Warn  ; Enable warnings to assist with detecting common errors.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.


;~ Except where noted on the functions page, variables do not need to be declared;

;~ Variables come into existence simply by using them (and each variable starts off empty/blank).
if (a = "") ; the variable "a" comes into existence simply by using it in a comparison
if b = ; the variable "b" comes into existence simply by using it in a comparison
c := ; the variable "c" comes into existence simply by using it when you assign it (even assign it to contain nothing as in this example)
msgbox % "a: " . a . "`nb: " . b . "`nc: " . c . "`nThe variables came into existence, but their contents starts off empty/blank."


;~ Variable types: AutoHotkey has no explicitly defined variable types. However, a variable containing only digits (with an optional decimal point) is automatically interpreted as a number when a math operation or comparison requires it. (To improve performance, numbers are cached internally to avoid conversions to/from strings.)
a := 5 ; the variable "a" contains only a digit, but is not interpreted to be a number yet
b := a + 5 ; the variable "a" is required for a math operation (a + 5) so it is interpreted to be a number type variable because it's used in a math operation on this line
msgbox % "b is equal to: " . b . "`nThis is because the variable ""a"" was interpreted as a number, and added to 5 (b := a + 5) to evaluate to 10."
if a = 5 ; the variable "a" is required for a comparison (if a = 5) on this line, since the variable "a" contains only digits, it is interpreted as a number
{
	msgbox % "yes, the variable ""a"" is equal to 5!"
}
else
{
	msgbox % "no, the variable ""a"" is not equal to 5!"
}

; Here is an example where a variable does not contain only digits, so it's not interpreted as a number type variable when used in math operations or comparisons.
a := 5grams ; the variable "a" contains contains a digit, but also non-digits : it is not interpreted yet
b := a + 5 ; the variable "a" is required for a math operation (a + 5) but "a" does not contain only digits, so it's not interpreted as a number, the math operation fails silently and "b" evaluates to nothing (empty)
if a = 5 ; the variable "a" is required for a comparison (if a = 5) on this line, since the variable "a" contains only digits, it is interpreted as a number
{
	msgbox % "yes, the variable ""a"" is equal to 5!"
}
else
{
	msgbox % "no, the variable ""a"" is not equal to 5!"
}

;~ Variable scope and declarations: With the exception of local variables in functions, all variables are global; that is, their contents may be read or altered by any part of the script. 
a := "test"
gosub MyLabel


;~ To access variables created outside functions and use them inside functions they must be declared global variables outside of functions.
global a := "test"
msgbox % myFunction()
myFunction() {
	b := a . " worked"
	return b
}

;~ Alternatively, functions themselves can be declared global so that they can use variables created outside of functions.
a := "test"
msgbox % myFunction1()
myFunction1() {
	global ; this function is declared a global function by placing "global" on the first line inside of the function
	b := a . " worked"
	return b
}


;~ Variable names: Variable names are not case sensitive (for example, CurrentDate is the same as currentdate).
CurrentDate := "December 5th, 1805"
currentdate := "January 6th, 1906" ; The currentdate is the same as CurrentDate, so CurrentDate/currentdate both contain "January 6th, 1906" on the second line.
msgbox % "currentdate: " . currentdate . "`nCurrentDate: " . CurrentDate . "`ncURRENTdATE: " . cURRENTdATE

;~ Variable names may be up to 253 characters long and may consist of letters, numbers and the following punctuation: # _ @ $
abc := "test 1"
123 := "test 2"
#a# := "test 3"
_b_ := "test 4"
@c@ := "test 5"
$d$ := "test 6"
msgbox abc: %abc% `n123: %123% `n#a#: %#a#% `n_b_: %_b_% `n@c@: %@c@% `n$d$: %$d$%
return

MyLabel:
msgbox % a ; the variable "a" is found because variables created outside functions are accessible everywhere in the script except inside functions.
return
:arrow: itros "ylbbub eht tuO kaerB" a ni kcuts m'I pleH

Return to “Tutorials”

Who is online

Users browsing this forum: No registered users and 8 guests