o /| . *
. 0 ( / | ,. ,-. .
. * `/._|,(_.\ \ \ ,-\ . *
(_.' l_ \ `-´\ `-´\ o
AquaHotkey is a class-prototyping library that lets you easily rewrite
built-in types like Array, String or Map to match your own style
and preferences.
"Hello, World!".SubStr(1, 7).Append("AquaHotkey!").MsgBox()Setting up this one-liner to work is really easy! Just do the following:
#Include <AquaHotkey>
class StringUtil extends AquaHotkey {
class String {
SubStr(Start, Length?) {
; `this` - a string instance
return SubStr(this, Start, Length*)
}
Append(Str) {
return (this . Str)
}
MsgBox() {
MsgBox(this)
}
}
}What you see is an extension class that contains custom methods .SubStr(),
.Append() and .MsgBox() for the type String. As soon as the script loads,
the extension class "pastes" its contents into the specified target, in this
case String.
Getting started:
Optional stuff:
Also see:
With regular AutoHotkey libraries, you usually end up with piles of utility functions. you must remember which function works on which type, invent naming rules, or write big checks like "if this is a string, else if this is an array ...". Works, but it becomes clunky very quickly.
AquaHotkey is a framework for "class prototyping" in AutoHotkey v2 - the ability
to add properties and methods to built-in types like String and Array.
Let's say we want to add some simple array methods to reuse across your
script. Normally, you'd start by calling Array.Prototype.DefineProp(...), and
defining everything as a bunch of global functions. Like this:
Array.Prototype.DefineProp("ForEach", { Call: Array_ForEach })
Array.Prototype.DefineProp("Contains", { Call: Array_Contains })
Array_ForEach(this, Action, Args*) {
GetMethod(Action)
for Value in this {
Action(Value?, Args*)
}
return this
}
Array_Contains(this, ExpectedValue) {
for Value in this {
if (Value == ExpectedValue) {
return true
}
}
return false
}This works, but doing it manually is difficult, tedious, and very error-prone.
In AquaHotkey, all of this is done declaratively using "extension classes" that specify which properties should be added to which targets.
class ArrayUtils extends AquaHotkey {
class Array {
ForEach(Action) { ... }
Contains(Value) { ... }
}
}The greatest advantage of this is being exposed to regular class-based syntax. Instead of implementing everything as global functions, you can write things as if you had direct access to the built-in class, which is much more intuitive and feels a lot more natural.
Instead of huge "do-everything" functions, you can break up things into smaller parts. Like this:
class ToString extends AquaHotkey {
class Number {
ToString() => String(this)
}
class String {
ToString() => this
}
class Array {
ToString() {
Result := "["
for Value in this {
; ...
}
Result .= "]"
return Result
}
}
class Object {
ToString() { ... }
}
}Strings get string methods, arrays get array methods, and so on. The objects themselves "know" what to do.
We've just successfully made String(Value) a feature that works on (almost)
all data types.
This type of meta-programming uncovers a beautiful, but yet still vastly unexplored part of AutoHotkey, and it's my job to be your tour guide. If you're interested, you can check out AquaHotkeyX, where these patterns are taken to their extreme.
When you're done writing a class, you can put it into a separate file, and
#Include it across multiple scripts.
StringUtils.ahk:
class StringUtils extends AquaHotkey {
class String {
Rep(Pat, Rep) => StrReplace(this, Pat, Rep)
Contains(Pat) => InStr(tihs, Pat)
}
}MyScript.ahk:
#Include <StringUtils>
Str := "Hello, world!".Rep("l,", "p").Rep("d", "m").Rep("!", "?")
"foo".Contains("o") ; trueYou can start very small, one quick fix after another. And sooner than you think, it'll grow into your own language on top of AutoHotkey.
AutoHotkey v2 is a prototype-based language, exactly like JavaScript. Everything has an internal link to another object called its prototype, and so on, forming a "prototype chain".
Example - the number 42:
42
`- Integer.Prototype
`- Number.Prototype
`- Primitive.Prototype
`- Any.PrototypeMore interestingly, you can modify these prototypes to change the behaviour of any deriving object.
The concept behind class prototyping revolves around making changes to the internal prototype objects to add properties and methods:
; add a `.Length` property for strings
({}.DefineProp)(String.Prototype, "Length", { Get: StrLen })We've just successfully added a Length property to String.Prototype, which
is the prototype object of all strings.
You can now use the Length property on strings:
MsgBox("foo".Length) ; 3Thanks to AquaHotkey, this is no longer tedious manual work. Everything happens declaratively, and with simple "class syntax".
A unique and modern standard batteries-included library that builds on top of AquaHotkey.
- Heavily chainable method calls
- Extensive use of functional programming patterns
- Designed for maximal elegance and conciseness
#Requires AutoHotkey >=v2.0.5
#Include <AquaHotkeyX>
; Map { 4: ["kiwi", "lime"], 5: ["apple"], 6: ["banana"] }
Array("banana", "kiwi", "apple", "lime").Group(StrLen)
; <[1, 2, 3], [2, 3, 4], [3, 4, 5]>
Range(5).Stream().Gather(WindowSliding(3))For a quick overview, see API Overview.
Made with love and lots of caffeine.
- 0w0Demonic