Facade Functional Programming Suite

Post your working scripts, libraries and tools for AHK v1.1 and older
robodesign
Posts: 934
Joined: 30 Sep 2017, 03:59
Location: Romania
Contact:

Re: Facade Functional Programming Suite

23 Jan 2020, 08:27

I did not state you are the best of all AHK programmers ;-)... nor did I try to suggest it. And yes, there are a few highly advanced programmers here in AHK, but I bet they do not know only AHK.

My stance is that by knowing only AHK, it is hard to become a good developer, because it screws up the mindset, as you suggested. Luckily, I am not one of those relying on Goto/Gosub ^_^ . My worst offence is to rely on globals ^_^ .

And yes, I agree , anyone who can make a sensible contribution to the AHK community is definitely welcome. And... I emphasize again, your library is without doubt a good one... It only requires people to adjust a bit their mindset..

Anyways, good luck !

Best regards, Marius.
-------------------------
KeyPress OSD v4: GitHub or forum. (presentation video)
Quick Picto Viewer: GitHub or forum.
AHK GDI+ expanded / compilation library (on GitHub)
My home page.
guest3456
Posts: 3462
Joined: 09 Oct 2013, 10:31

Re: Facade Functional Programming Suite

23 Jan 2020, 10:17

[Shambles] wrote:
22 Jan 2020, 20:15
Most of the time when you think you want a changing thing, what you really want is to build new code to do different things. Getting a handle on this is hard.
can you give an example of this? i guess we're really asking you to teach us when and how to do functional programming, which is why this is all so difficult

[Shambles]
Posts: 100
Joined: 20 May 2014, 21:24

Re: Facade Functional Programming Suite

23 Jan 2020, 14:35

guest3456 wrote:
23 Jan 2020, 10:17
[Shambles] wrote:
22 Jan 2020, 20:15
Most of the time when you think you want a changing thing, what you really want is to build new code to do different things. Getting a handle on this is hard.
can you give an example of this? i guess we're really asking you to teach us when and how to do functional programming, which is why this is all so difficult
You should do functional programming when what you are doing is pure processing, not mutating 'the state of the world' (a phrase often used in functional programming but easy to understand if you are familiar with video games and can imagine storing the state of the video game world in a variable) and not performing I/O, and this should describe the vast majority of most realistic code. Most code is about processing. By chunking most of your side effects near execution entry and exit points (the main routine and event handlers) most of your code should not be 'broken up' by side effects and can therefore be easy to describe functionally (and thus be easy to test, debug, reuse, and (for a compiler) optimize).

That is slightly misleading. You really want to know several declarative programming paradigms (e.g. functional programming, logic programming, dataflow programming, etc.) and choose the one that fits your problem best. It won't always be functional programming.

Sadly, most programming paradigms are not taught any more, only imperative and OOP, which are the most dangerous and laborious. If you want to know what "declarative programming" means, a standard definition goes something like "the programmer describes what to do and the computer figures out how to do it", but what that means in practice (since all programming is 'describing how to do it') is the control flow and resource management is as implicit as possible (i.e. declarative programming uses few, if any, branches or loops and tends to use tracing garbage collection).

The reason most programming paradigms are no longer taught is because few programming languages can efficiently describe more than one and it is difficult to describe whole programs in any single declarative paradigm. The Lisp family of programming languages are the only ones that are especially well-suited to embedding multiple programming paradigms (because they have extremely simple, extremely regular syntax, which can be used to describe anything, and macros, which act as mini-compilers), allowing you to use whichever paradigm describes a portion of your program best and share data easily between the different paradigms. The Lisp family of programming languages fell out wide use during the AI winter.

But functional programming does not require much more infrastructure than imperative programming, so it is relatively easy to implement in conventional programming languages and mingle with code in conventional programming languages (like how Facade works).

As for the 'when you want a changing thing'...

When an imperative programmer wants to create some 'powerful' code they usually do it by mutating pointers or references to other code. In other words, they do it much like functional programming's higher-order functions, but instead of having the code accept the other code to call (in the form of said pointers or references) they have some data structure they change that stores the code to call. This is error-prone for the same reason changing any data structure is error-prone.

When a functional programmer wants to create some 'powerful' code they usually do it by constructing new code at run-time and calling it. Functions are just normal values after all. They can be passed as arguments and returned as values, and the combinators in the Func library do just that.

So when you want something to change its value under complex circumstances, instead of literally detecting the circumstances and mutating some variables, you could construct some code, at run-time if necessary (e.g. when the 'complex' part is the desired behavior can only be known based on user input), that accepts the 'circumstances' as arguments, and calculates the result. What was once a bunch of tests in branches that mutated some variables now becomes a reference to a function that computes a value.
guest3456
Posts: 3462
Joined: 09 Oct 2013, 10:31

Re: Facade Functional Programming Suite

23 Jan 2020, 21:48

[Shambles] wrote:
23 Jan 2020, 14:35
You should do functional programming when what you are doing is pure processing, not mutating 'the state of the world' (a phrase often used in functional programming but easy to understand if you are familiar with video games and can imagine storing the state of the video game world in a variable) and not performing I/O, and this should describe the vast majority of most realistic code. Most code is about processing. By chunking most of your side effects near execution entry and exit points (the main routine and event handlers) most of your code should not be 'broken up' by side effects and can therefore be easy to describe functionally (and thus be easy to test, debug, reuse, and (for a compiler) optimize).

That is slightly misleading. You really want to know several declarative programming paradigms (e.g. functional programming, logic programming, dataflow programming, etc.) and choose the one that fits your problem best. It won't always be functional programming.
i mean i guess ill use my example, my main script manipulates multiple game windows.. i'm doing image and pixel searches to determine the game state, and then moving windows around or changing z-order or hotkeys which send clicks to the game window, depending on the state that my image searches detect. half way through the life of the script, i decided to try to write unit tests, and found it nearly impossible, i had to create gui's to act as fake game windows etc.

my main script is just a big timer loop that keeps doing the image/pixel processing to determine what actions to take. i have a very difficult time trying to figure out how to do this the functional way, but i think its more my lack of knowledge that is the limiter

maybe something like this (pseudo):

Code: Select all

;before
WinGet, list, list
Loop, Parse, list, `,
   Process(A_LoopField)...
   
;after
WinGet, list, list
Loop, Parse, list, `,
   Array.Push(A_LoopField)      ; first convert from CSV to Array
Map(Func("Process"), Array))
not sure

[Shambles] wrote:
23 Jan 2020, 14:35
So when you want something to change its value under complex circumstances, instead of literally detecting the circumstances and mutating some variables, you could construct some code, at run-time if necessary (e.g. when the 'complex' part is the desired behavior can only be known based on user input), that accepts the 'circumstances' as arguments, and calculates the result. What was once a bunch of tests in branches that mutated some variables now becomes a reference to a function that computes a value.
this sounds important, but i'm not sure i understand it. can you come up with a quick example of the before and after?

[Shambles]
Posts: 100
Joined: 20 May 2014, 21:24

Re: Facade Functional Programming Suite

24 Jan 2020, 10:22

This might seem like it is rambling. I am trying to lead you through the necessary insights to understand the answers you asked for, and I am trying to answer questions I believe you need to ask in addition to the ones you asked.
guest3456 wrote:
23 Jan 2020, 21:48
i mean i guess ill use my example, my main script manipulates multiple game windows.. i'm doing image and pixel searches to determine the game state, and then moving windows around or changing z-order or hotkeys which send clicks to the game window, depending on the state that my image searches detect. half way through the life of the script, i decided to try to write unit tests, and found it nearly impossible, i had to create gui's to act as fake game windows etc.

my main script is just a big timer loop that keeps doing the image/pixel processing to determine what actions to take. i have a very difficult time trying to figure out how to do this the functional way, but i think its more my lack of knowledge that is the limiter
Niklaus Wirth wrote a textbook with this title:
Algorithms + Data Structures = Programs

The title is insightful. The reason we should teach students algorithms and data structures is because those are what are used to describe programs, and the more of them a programmer knows, the easier it is for that programmer to describe programs and the more efficient their programs will be.

Textbooks that teach programming paradigms are rare, and courses that use them are rarer. Advanced Programming Language Design is the best one that I know of. The reason we should teach students programming paradigms is because using a suitable programming paradigm tends to make writing part of a program easier, because it makes parts of the algorithms implicit, so the original author does not have to write them and maintainers do not have to read or maintain them.

Using the right paradigm occasionally makes enough of a difference to seem to grant magical powers. For example, did you know that if you really understand OOP, you can write software that is impractical to hack or infect with malware? Capability-Based Computer Systems will teach you how.

One way to think of a programming paradigm is the operational (what it does in the machine) way I just described (implicit control flow and resource management, and sometimes error handling).

Another way to think of a programming paradigm is as a perspective.

One thing that makes programming difficult is humans' lack of foresight. We tend to not think ahead and have a very poor understanding of the implications of what we do and do not do. That is why we write programs that eventually corrupt their state.

Is there anywhere in human experience that these problems are minimized? Yes! Conventional mathematics (the kind taught in elementary school).

Conventional mathematics models things as permanent relationships. When solving an equation, the value of a variable never changes. You might solve it again for different values (or in programmer-speak "you might pass it different arguments") but within the same context (or in programmer-speak "call") it never changes (or in programmer-speak "the same variable might have different values in different calls but never in the same call"). Time stands still.

Functional programming is what happens when you use this perspective when programming. You stop thinking about change over time and start thinking about permanent relationships.

But change over time does occur and it matters! A computer that does not react to input is just an inefficient space heater!

Early functional programmers (e.g. those using Lisp in the 1960s) learned to separate the code that performs side effects from the code that performs processing. They used the functional programming paradigm where they could (for processing) and the imperative programming paradigm when they had to (for retaining the state of their program across events and performing I/O).

This was before "publish or perish" flooded academic journals with junk research, universities (and thus researchers) were defunded by governments because they were teaching students to think for themselves instead of being obedient drones (and then the universities became businesses that charge students as much as the market will bear for the training businesses should be providing on the job), and corporations invented an endless stream of products to sell (often creating the problems they get paid to solve). The end result of those malignant processes is a tech industry in decline resulting from a strange sort of amnesia from neophilia and dogmatic thought from marketing. If you are old enough and you are willing to pay attention to unpleasant truths, you will start noticing things like the current cloud computing craze sounds a lot like the bad old days of rent to never own time-sharing computing and functional reactive programming from the 1990s sounds a lot like some new researchers trying to take credit for dataflow programming from the 1960s.

Lets say you are in the throes of this malaise. Maybe you are someone that wants to sell a product that claims functional programming is a silver bullet. Maybe you are someone that actually believes functional programming is a silver bullet.

Is there some way to shoehorn side effects into the functional programming paradigm? Yes... Conventional mathematics can model time. Imagine a conventional graph where the X axis represents time moving from 0 at the beginning of a process onward and Y represents the function's value at that point in time. The function is still a fixed relationship between X and Y.

How does this translate to programming?

The changing real world can be thought of as that X axis that is the argument passed to your program that you think of as a function that produces output that can be thought of as the Y value at that point on the X axis. This is what is meant by "the state of the world".

Some modern programming languages, like Haskell, make you model things this way.

Haskell would make you use its complex type system and have documentation that claims programs written in Haskell are somehow pure (as in referentially transparent) despite clearly performing side effects by playing word games with what is meant by program and side effect. This trick involves claiming that programs written in Haskell merely compute the good and pure value representing the state of the world while the evil and dirty runtime system (which is somehow 'not Haskell') is what actually performs the side effects the state of the world describes. This argument is vacuous. It would define C as a purely functional programming language and allow one to argue it 'just has better syntax for describing the state of the world' (i.e. performing side effects). The reason programmers should care about side effects is that they happen (if you remove them, the program's meaning changes), when they happen (if you delay them, the program's meaning changes), and the order they happen in (if you reorder them, the program's meaning changes) matters, and any compiler's optimizer is going to have to treat Haskell code with side effects the same as imperative code with side effects.

The Haskell code would be arranged exactly like the code good functional programmers would have written in older programming languages.

What would an AutoHotkey-syntaxed outline of that look like?

A command-line program (like a compiler) would look like this:

Code: Select all

; Implement ReadWorld(A_Args) here.

; Implement ComputeWorld(World) here.

; Implement WriteWorld(World) here.

Main(A_Args)
{
    exit WriteWorld(ComputeWorld(ReadWorld(A_Args)))
}

Main(A_Args)
An event-driven program (like a video game) would look like this:

Code: Select all

; Initialize World here.

; Implement ReadWorld(World) here.

; Implement ComputeWorld(World) here.

; Implement WriteWorld(World) here.

TimerEventHandler()
{
    local
    global World
    World := ComputeWorld(ReadWorld(World))
    WriteWorld(World)
}

SetTimer, TimerEventHandler
In both kinds of programs ReadWorld performs input, ComputeWorld performs processing, and WriteWorld performs output. In the command-line program ReadWorld receives the command-line arguments and might perform additional input (e.g. read some files). In the event-driven program ReadWorld receives the old state of the world and probably performs additional input (e.g. reading the window's message queue for keyboard and mouse input). The command-line and event-driven programs differ in that the command-line program contains code that is executed once and the event-driven program contains code that is executed repeatedly.

These examples are unrealistic. They have been simplified to be easy to understand. Notably, event-driven programs usually consist of many event handlers, and each of those would have different code for input, processing, and output. Most hardware has different interrupts (and therefore different interrupt handlers) for updating the screen, updating the sound queue, reacting to key presses, reacting to mouse events, reacting to USB input (like gamepads), reacting to input on network interfaces, and so on. All of them would update the state of the world. Many of them would only add pending input to the state of the world, not perform any output, or only update an output queue, not perform any input.

Your program should look a lot like the event-driven program just described.

Why bother structuring it that way? You answered this question yourself when you found out how hard it was to test and debug code where I/O is mingled with processing.

Representing your program's state as a value also makes some interesting things, like time traveling debugging, possible.

I suggest writing programs this way:
1. Figure out what data your program requires to do what you want.
2. Figure out what you want to do to that data. This tells you what algorithms to use.
3. Knowing what algorithms to use tells you what data structures to use (e.g. searching a dictionary is efficient, searching an array is not).
4. Knowing what data structures to use tells you how to represent your 'world'.
5. Write the simplest possible output procedure(s) you can for your main procedure or event handlers. Test them. Debug them. Try to never change them again.
6. Write the simplest possible input procedure(s) you can for your main procedure or event handlers. Test them. Debug them. Try to never change them again.
7. Write the processing function(s) to update the world for your main procedure or event handlers.

Why this order?

Some of it, like the parts that tell you what algorithms and data structures you need, just have to be done that way.

Output procedures are easier to test and debug than input procedures. It is hard to know if you are receiving input if you cannot see or hear anything.

If you follow this plan, the code that is inherently difficult to test, the I/O code, is as simple as possible (and thus unlikely to be defective) and need not be regularly retested and redebugged.

It is easier to stay motivated when you can see progress being made, and the I/O code is what lets you literally see progress being made.

One problem remains. How do you test and debug a world changing function? It no longer performs any I/O, but it shoves all the complexity into one place, so it is likely to have defects.

Most good programming languages have a REPL. If your only experience programming is with AutoHotkey, this will be foreign to you. REPL stands for "read, evaluate, print, loop". They read code you type into them, evaluate it (i.e. execute it to get the resulting value), 'print' the result to the screen, then wait for more input. You might be wondering what it means to 'print a value to the screen'. Long ago, people programmed using something called a teleprinter that looked a lot like an old fashioned typewriter but was connected to the computer so that both the programmer and the computer could type. When either party finished typing something, the paper would scroll up. When you know that, the way the REPL works becomes more obvious. But what about values that aren't numbers or strings? Data structures are usually represented as the equivalent literal syntax. Things that have no equivalent literal syntax, like a function object or closure, are usually represented as something surrounded by 'angle brackets'.

A REPL session for an AutoHotkey-syntaxed programming language might look something like this:

Code: Select all

2 + 2
; 4
5 / 0
; Error: division by zero
Foo()
{
    X := []
    X.Push(1)
    X.Push(2)
    X.Push(3)
    return X
}
Foo()
; [1, 2, 3]
Func("Foo")
; <function>
In most programming languages you would break your world changing function down into a lot of separate functions that it would call, and you would test these at the REPL as you wrote each one. The world itself, along with any changes to it, would be observable because you could see the data structure at the REPL.

But AutoHotkey does not work like that. So what do you do?

Yeah... :roll: That is one of many reasons I do not enjoy programming in AutoHotkey despite it being useful and being good at it.

Another major reason is v1 does not actually report errors like in that fictional REPL session above. Luckily, someone wrote Facade.

What I do is write some throwaway visualization code for what I am working on.

But there really is no way to visualize a function value. So what do you do?

Yeah... :? That is an excellent reason to get all the function-constructing code written once, debugged, and never write such code again. Luckily, someone wrote Facade.

Hopefully this helps.
guest3456 wrote:
23 Jan 2020, 21:48
[Shambles] wrote:
23 Jan 2020, 14:35
So when you want something to change its value under complex circumstances, instead of literally detecting the circumstances and mutating some variables, you could construct some code, at run-time if necessary (e.g. when the 'complex' part is the desired behavior can only be known based on user input), that accepts the 'circumstances' as arguments, and calculates the result. What was once a bunch of tests in branches that mutated some variables now becomes a reference to a function that computes a value.
this sounds important, but i'm not sure i understand it. can you come up with a quick example of the before and after?
Your program is an example.

There are simple examples in Facade's documentation for Func_Applicable and Func_CIf.

Func_Default's code could be considered an example. Some parts of Facade are 'written in Facade', and it is one such part.
Last edited by [Shambles] on 02 Oct 2021, 16:57, edited 2 times in total.
guest3456
Posts: 3462
Joined: 09 Oct 2013, 10:31

Re: Facade Functional Programming Suite

24 Jan 2020, 14:25

[Shambles] wrote:
24 Jan 2020, 10:22
Hopefully this helps.
you are clearly very educated in computer science.. this reminds me of my first year at university when they were teaching us Scheme, and unsurprisingly, i hated it, couldn't get my head around it, i just wanted to build real world things, not all that theoretical stuff. its funny how this is coming full circle for me.

i may just have to start writing things from scratch following your framework, and see where it leads me
[Shambles] wrote:
24 Jan 2020, 10:22
These examples are unrealistic. They have been simplified to be easy to understand. Notably, event-driven programs usually consist of many event handlers, and each of those would have different code for input, processing, and output. Most hardware has different interrupts (and therefore different interrupt handlers) for updating the screen, updating the sound queue, reacting to key presses, reacting to mouse events, reacting to USB input (like gamepads), reacting to input on network interfaces, and so on. All of them would update the state of the world. Many of them would only add pending input to the state of the world, not perform any output, or only update an output queue, not perform any input.
yeah, see all of these interrupts which update the state of the world seem like the real world stuff that we are dealing with most of the time in AHK, which is why most of the people here are having such a hard time with your library. whereas it seems like functional programming is much more prevalent in something like Javascript

[Shambles] wrote:
24 Jan 2020, 10:22
Most good programming languages have a REPL. If your only experience programming is with AutoHotkey, this will be foreign to you. REPL stands for "read, evaluate, print, loop". They read code you type into them, evaluate it (i.e. execute it to get the resulting value), 'print' the result to the screen, then wait for more input. You might be wondering what it means to 'print a value to the screen'. Long ago, people programmed using something called a teleprinter that looked a lot like an old fashioned typewriter but was connected to the computer so that both the programmer and the computer could type. When either party finished typing something, the paper would scroll up. When you know that, the way the REPL works becomes more obvious. But what about values that aren't numbers or strings? Data structures are usually represented as the equivalent literal syntax. Things that have no equivalent literal syntax, like a function object or closure, are usually represented as something surrounded by 'angle brackets'.

A REPL session for an AutoHotkey-syntaxed programming language might look something like this
i tried to make a REPL for AHK by using AHK_H to run the code in a new thread.. here was a very basic implementation:
https://www.autohotkey.com/boards/viewtopic.php?p=124013#p124013
but by your definition above, it doesn't look like what i wrote was a REPL, but rather more like a dynamic executor

[Shambles]
Posts: 100
Joined: 20 May 2014, 21:24

Re: Facade Functional Programming Suite

24 Jan 2020, 17:42

guest3456 wrote:
24 Jan 2020, 14:25
you are clearly very educated in computer science.. this reminds me of my first year at university when they were teaching us Scheme, and unsurprisingly, i hated it, couldn't get my head around it, i just wanted to build real world things, not all that theoretical stuff. its funny how this is coming full circle for me.

i may just have to start writing things from scratch following your framework, and see where it leads me
I am mostly self-taught. I read computer science journal articles (mostly from CiteSeer and professors' personal home pages) for fun.

Most people want to do things without thinking first. That is how we got into this situation where thousands people's ids and financial information are stolen every year because some greedy, shortsighted novice decided they did not need to learn anything before making a web storefront for some business, err, I mean some praiseworthy 'entrepreneur' made an innocent and completely unforeseeable mistake (like needlessly storing and then not encrypting customers' data AGAIN). There really is no substitute for knowing what you are doing, and learning how to make things well is not a waste of time.

If you are a programmer that is not interested in writing good code, you must be interested in writing bad code.

The point in doing things the right way is to reduce risk.
guest3456 wrote:
24 Jan 2020, 14:25
yeah, see all of these interrupts which update the state of the world seem like the real world stuff that we are dealing with most of the time in AHK, which is why most of the people here are having such a hard time with your library. whereas it seems like functional programming is much more prevalent in something like Javascript
AutoHotkey and JavaScript are more alike than they are different. As far as I can tell, most of AutoHotkey's design was lifted from Microsoft's ScriptIt (some of the bad syntax), JavaScript (similar but different type system insanity, almost identical Object, and the misunderstanding of Self's prototype-based OOP), and PHP (intentionally ignoring errors and continuing execution, and shallow wrapping of win32 and other APIs leading to inconsistent and overly complicated interfaces). Those programming languages would have been excellent choices in a programming language design course to show what not to do. Basing a programming language on only their bad parts is sadistic!

Brendan Eich intended to use Scheme for client-side programming of web browsers, but he only had 10 days to implement it and Netscape Communications' management forced him to use syntax similar to Java midways through. So of course he made a mess! He did not have time to do anything right and the catastrophic change mid-project is what lead to this and this. And we will all be paying the price for their lack of foresight for the foreseeable future (one does not simply replace JavaScript).

The difference between AutoHotkey and JavaScript is there are good parts to JavaScript. It is brain damaged (with a thoroughly hosed type system, no macros because of complex syntax, etc.) Scheme, but some parts of Scheme (like proper closures, some higher-order functions, and tracing garbage collection) still work in it.

Nope! JavaScript is not a language you should admire, nor is it one used for vastly more intellectual tasks.

For goodness sake, 'web apps' work by animating something that was intended to be a document format (it is right there in the HyperText Markup Language name)! This is like designing triangular wheels! It is astounding any of it works at all!

As for whether using Facade is practical or not, I have repeatedly stated that it performs general purpose processing, so it would be practical if you are performing any processing. I did not include any domain-specific features. It is basically just a safer (reports errors, better designed) version of AutoHotkey written in AutoHotkey.
guest3456 wrote:
24 Jan 2020, 14:25
i tried to make a REPL for AHK by using AHK_H to run the code in a new thread.. here was a very basic implementation:
https://www.autohotkey.com/boards/viewtopic.php?p=124013#p124013
but by your definition above, it doesn't look like what i wrote was a REPL, but rather more like a dynamic executor
Yes, the point in a REPL is to be able to load some code (e.g. by calling some "load" procedure or using an "include" statement) and data (by calling procedures to load data from files into variables) and manipulate both. It is not intended that you do most of your coding or processing in the REPL. Doing that is possible (by cutting and pasting working code into files and calling procedures to write data from variables into files), just uncomfortable. Whatever is in the REPL is lost when it is closed. It is intended for exploratory programming. You can use it to see if some code works the way you expect (e.g. Are you understanding the documentation correctly?) and to test if your ideas are bad (e.g. Is your code defective?).

AutoHotkey lacks both a REPL and the necessary infrastructure to support one. To support a REPL, you need the ability to dynamically load code into the same environment (read), an eval function to evaluate code in an environment (eval), and a standard interface built-in and user-defined types can use to define how to represent themselves as text (print). AutoHotkey lacks all of those.
[Shambles]
Posts: 100
Joined: 20 May 2014, 21:24

Re: Facade Functional Programming Suite

03 Oct 2020, 04:01

I fixed a race condition. It could occur when a Stream (an immutable type) was shared by more than one 'thread'. It was caused by memoization.

I also added Dict_KeyValuePred(KeyPred, ValuePred) and Dict_KeyValueFunc(KeyFunc, ValueFunc).

Nothing has changed that should break code for some time.
[Shambles]
Posts: 100
Joined: 20 May 2014, 21:24

Re: Facade Functional Programming Suite

06 Oct 2020, 18:14

I fixed defects in the hashing of floats (also in HashTable) and truncation in Op_Integer(X). They were caused by AutoHotkey's truncation being defective (e.g. 1.1e1 & -1 is 1 instead of 11).
[Shambles]
Posts: 100
Joined: 20 May 2014, 21:24

Re: Facade Functional Programming Suite

10 Dec 2020, 16:54

I fixed defects in the handling of -inf, inf, and nan in the Op and Math libraries and the associated input validation. They were caused by me forgetting those values exist.
SOTE
Posts: 1426
Joined: 15 Jun 2015, 06:21

Re: Facade Functional Programming Suite

12 Dec 2020, 02:17

[Shambles] wrote:
24 Jan 2020, 10:22
This might seem like it is rambling. I am trying to lead you through the necessary insights to understand the answers you asked for, and I am trying to answer questions I believe you need to ask in addition to the ones you asked.

Niklaus Wirth wrote a textbook with this title:
Algorithms + Data Structures = Programs

The title is insightful. The reason we should teach students algorithms and data structures is because those are what are used to describe programs, and the more of them a programmer knows, the easier it is for that programmer to describe programs and the more efficient their programs will be.

Textbooks that teach programming paradigms are rare, and courses that use them are rarer. Advanced Programming Language Design is the best one that I know of. The reason we should teach students programming paradigms is because using a suitable programming paradigm tends to make writing part of a program easier, because it makes parts of the algorithms implicit, so the original author does not have to write them and maintainers do not have to read or maintain them.
...
I think this post deserves a 10. Just wanted to add, for anybody interested, that you can download Niklaus Wirth's Algorithms and Data Structures for free and legally at (https://people.inf.ethz.ch/wirth/AD.pdf).

I prefer his original book written in Pascal (the language later evolved into Object Pascal/Delphi), from a kind of historical perspective, but there isn't a link to it that I know of or could find. For those that know some Pascal, they will find Oberon is close enough. Both languages and the content of the book are easy enough to understand and everything is well explained, so interested AHKers should find it an interesting or enjoyable read. For those that don't know, Niklaus Wirth is a Turing Award winner, and the chief designer/creator of Algol W, Pascal, Modula, and Oberon.
[Shambles]
Posts: 100
Joined: 20 May 2014, 21:24

Re: Facade Functional Programming Suite

13 Dec 2020, 16:10

I made several changes to Facade:
  • I removed the complemented relational predicates Op_Ne(Args*), Op_IdNe(Args*), and String_CiNe(Args*).
  • I included Func_CNotRel(RelPred) to make it possible to construct complemented relational predicates at run-time.
  • Dict_Difference(Dicts*) is now consistent with Op_Sub(Numbers*) when passed 1 argument.
  • I renamed Stream_From(Start [, Step]) to Stream_HbIntvl(Start [, Step]) and Stream_Range(Start, Stop [, Step]) to Stream_HoIntvl(Start, Stop [, Step]).
  • I renamed Dict_HasKey(Key, Dict) to Dict_Has(Key, Dict) and Dict_HasKeyIn(Path, Dict) to Dict_HasIn(Path, Dict).
I rarely used the complemented relational predicates. They also do not chain the relation like the other relational predicates. If they did, Op_Ne(0, 0, 1) would be false because 0 = 0, but that would be surprising because Op_Eq(0, 0, 1) would also be false because 0 ≠ 1. I guess that is why Lisp dialects do not have them.

I added the combinator to reconstruct them in case they are ever needed. This is also a simpler, more powerful design. For example, you can now ask if an Array is not naturally sorted by using Func_CNotRel(Func("String_IsNatSorted")).Call(YourArray*), though I have no idea why you would need to know that.

I had copied the design of Dict_Difference(Dicts*) from Clojure without being very critical. This design is more consistent.

Most of the Stream function names were copied from Scheme. "From" in a function name in Facade normally implies type conversion. The new names for the Stream functions are less ambiguous and more closely match the documentation that already existed.

One of the changes in AutoHotkey v2 that I approve of is the renaming of HasKey to Has, so this would ease porting to v2.

I am in the process of trying to make larger changes that would reduce the number of functions Facade's users have to learn and make it more efficient without reducing its power. I will not know if it will work until I finish implementing it, but that will require a complete rewrite. I am trying to maintain the existing library and make the improvements I can to it in case I fail.
[Shambles]
Posts: 100
Joined: 20 May 2014, 21:24

Re: Facade Functional Programming Suite

21 Dec 2020, 05:39

I updated the Type Checking library that Facade is built upon to more closely resemble current v2.

The old files were IsFuncObj.ahk, IsType.ahk, and Type.ahk. That might be helpful to know if you need to clean them out of your library directory before updating. Overwriting the *.ahk files for Facade should update it (the files only differ in their contents).

These changes should not be very user-visible. They made Facade faster and improved the error reporting for Op_GetProp(Prop, Obj and Op_Get(Key, Obj).
hasantr
Posts: 933
Joined: 05 Apr 2016, 14:18
Location: İstanbul

Re: Facade Functional Programming Suite

21 Dec 2020, 10:46

Thank you. Interesting good things. I am too new to understand enough. For now, I can hardly run anything without seeing an example. :)
I hope one day I will learn enough to understand what you want to tell.
[Shambles]
Posts: 100
Joined: 20 May 2014, 21:24

Re: Facade Functional Programming Suite

22 Dec 2020, 11:24

I corrected defects in Type Checking. Specifically, HasProp(Value, Name) was not reporting the existence of base on any type except Object and HasMethod(Value, Name) was reporting implicitly-defined methods on primitives (numeric values and strings), causing them to be mistaken for collections.

These problems did not affect Facade, which is the likely reason I did not notice them.

They were caused by the twisted logic (that you can witness in their code) necessary to try to make sense out of AutoHotkey's type system.

My apologies.

I wish you all a happy holiday season. Hopefully, next year will be better.
[Shambles]
Posts: 100
Joined: 20 May 2014, 21:24

Re: Facade Functional Programming Suite

29 May 2021, 03:37

I updated the Type Checking library that Facade is built upon to more closely resemble current v2 again. It should be slightly faster when working with COM objects.

I corrected a defect in Op_Integer(X) that corrupted the low bits of large integers. I introduced it when working around AutoHotkey incorrectly truncating floating point numbers.

I corrected a potential problem in Op_FloorDiv(X, Y) where precision could be lost when working with large integers.

Op_Round(X [, N]) should be a little faster.

I made minor improvements to the documentation.
biorpg
Posts: 2
Joined: 11 Dec 2014, 20:22

Re: Facade Functional Programming Suite

27 Jul 2021, 01:07

[Shambles] wrote:
24 Jan 2020, 17:42
I'll start by buttering you up and say that I like the additional functionality your library brings that was missing elsewhere for AHK, and agree with most of your forward-thinking concepts, and even a relatable portion of your politics.

Now, about AHK as it existed/exists without your library..

AHK is very good for doing what its name suggests, which makes it a good alternative to any other input macro tools, and I think this is also the majority of its users' first experience with it. This first experience is most likely going to be creating their own procedure to repeatedly or more easily perform a task that they have either grown tired of performing again and again manually, or need to perform many more times than they are willing to do, or perform much more efficiently by eliminating or otherwise automatically handling the un-intuitive or disruptive or tedious parts of a cumbersome windows application or windows itself. Let's assume this new user is one of the soon-to-be-lost sheep you express much concern about, and has no education or experience with software development. Using their likely more practiced web searching knowledge, they can quite quickly locate an existing script that does at least some of what they wish to accomplish. They can look the script over, and get a vague idea of the flow and try to relate that with the description that they had read which prompted them to choose it as the one to download, or if they are lucky some thoughtful comments may be included that much better describe its parts. Assuming their adaptation of this script requires more than adjusting strings and numbers, the next step will be to look up what they will need to add to suit their needs. This journey should be fairly short, depending on how much general computing experience the user possesses. They might follow a couple of rabbit holes, but with a search or three they should either find themselves at the AHK official documentation, a thread on a forum that has a helpful tip guiding someone to that documentation, or at the very same documentation in the AHK help in its install folder. They can search this help file with reasonable success in finding a mention or, better yet, function(s) that offer various ways to interact with such a thing. For the sake of argument, let's assume they failed to find what they searched for within the help file. This failure should have taken a few moments to transpire, involving possible variations in search terms, and some link navigation between topics they hope might have more guidance. Whether or not they found any such guidance in the body of the help text.. they will by now at least have seen examples of several frequently used functions and some other various function names visible in the search results and elsewhere. These would at least provide what could very likely be a missing link in their understanding of what AHK works with, or where it resides in the space between their input as a user, and the user interface that is to be interacted with. They can quickly confirm and complete their basic understanding of its use by way of trying out the various example scripts, reading the description of what each function and their parameters are, and experience how they can easily change these parameters to affect the result. Then, as this form of wanderous learning process goes, they will probably stumble their way along into one or more of the documentation's general or fundamental overviews, such as scripting flow. Perhaps they'll even read what a variable or a parameter is, and how to use them. Yes, after having used them already. This learning process is understandably chaotic, accident prone, and goes against all of the advice you can find from most developers. None of those seemingly messy or wrong aspects make any such learning process inefficient for the non-developer's use. Nor does it make AHK a bad choice for their goal, nor as a first experience with writing and editing code. Before long, they will have their desired script working in at least a partially complete, but quite visible manner. They'll likely fail to account for some possible changes in the process they had written from their memory of doing the same manually. The available functions to do all those things with AHK are documented in a manner friendly to such a user and goal, mostly providing individually complete actions that are presented in a manner that is easily associated with that which a user can normally see and do through normal interaction. If their clunky script that's always stuck when they return to their PC to check up on it isn't adequate enough, but the goal still proves wroth some more bouncy learn scripty time, then they'll probably go on to learn a good bit more about timing, window actions, various input methods and their benefits, conditional execution, so on and so forth. Had this user chosen a different language to achieve their goal, they would likely spend much more than a few days trying to grasp any working concept of it - that is, execute some example code that produces an observable result. Depending on available learning aids, this might be easy to accomplish, but will likely only result in the understanding that this script can process a thing, probably offering no form of low-hanging fruit or clear representation of how this script could help them reproduce all the steps of the procedure they want to automate. Actually venturing out to learn all the that might be necessary to work with windows, and send keyboard and mouse input to those windows at a certain coordinate, or even how to obtain the coordinates within the window for either making a record from their manual input, as well as for using as mouse input parameters in their script. To be realistic, this user would have given up already- if not while trying to get anything to run and act as a starting point to build an understanding from, then certainly at some point in the very long and confusing search for answers afterward. This might seem like this argument is painting the hypothetical user as overly simple or stupid, but in the modern sea of advertisement, sensationalism, corporate misdirection and interference, hundreds of languages that might accomplish their desired task or not, dozens of languages(or more) that can accomplish said task; an assortment of paid search results, as well as top 7, top 5, top 30, and top 10 best programming/scripting languages of 20-something, other eerily identical auto-generated crap articles, results containing text suspiciously identical to their search query, right down to the passionately written articles or forum posts that praise or berate the ease or usefulness of one of those languages, comparison of that language with others that are also presented in a heavily opinionated manner, all available for consideration and comparison at various levels of expertise, honesty, malice, cost, security, etc... all of which making it harder to find information they can place any confidence in than it is to follow an excessively long sentence with too many commas.

That's enough of my argument for the windows user, turned window-abuser, or the middle-aged skilled tradesman who's trade-specific knowledge and years of experience mean their tired old bones are worth training in the use of the industry's software, so that their experience can fill in the costly or unknown gaps that likely exist among the combined experience of a younger generation that has the same schooling and are more capable at using computers and the software; all of which are equally unlikely to be developers, and equally likely to benefit greatly from using AHK, and in this particular case, AHK specifically shines for these potential users for similar reasons why it is so attractive for your own use.

I was also going to make a case for the newbie developer using AHK as their first language. However, having typed all the stuff above, I now feel like typing not so much below. I'll specify the key points of my argument though. You say that AHK is a terrible language for starting to learn as a developer.
No it's not.
[Shambles]
Posts: 100
Joined: 20 May 2014, 21:24

Re: Facade Functional Programming Suite

30 Aug 2021, 09:04

biorpg wrote:
27 Jul 2021, 01:07
giant 💩💈
That is a lot of non sequitur!
[Shambles]
Posts: 100
Joined: 20 May 2014, 21:24

Recent Changes to Facade

30 Aug 2021, 15:08

I had been trying to improve Facade gradually. I had not yet posted about some of the changes I have already made because more were planned.

Useful feedback is rare. I am not seeking praise. Praise helps with motivation, but it usually lacks direction. I hoped to get feedback about features people liked, features people disliked, missing features, and defects. Those are sometimes actionable.

Lack of useful feedback has been highly demotivating. My attention has moved to other projects.

There have been a few complaints about documentation. I believe the documentation currently does a good job of precisely and concisely explaining what a construct is, what it does, and giving hints when to use it, but the documentation could be better.

There are several reasons the documentation has not been improved significantly:
  • There is only one person, a volunteer, working on this project, not an army of paid technical writers like at Microsoft or Oracle.
  • I am reluctant to devote significant effort to improving the documentation when the library may change. Documentation has to be rewritten when code changes.
  • Most of Facade is difficult to document with good examples because it uses types that have no string representation in AutoHotkey (e.g. functions, arrays, lists, streams, and dictionaries). If you try to MsgBox % Result , you will only see an empty string.
I am satisfied with the design and implementation of the Type Checking library for the moment. The design seems to be as good as AutoHotkey allows (e.g. numbers and strings must remain conflated). It is close to v2's design, so the difficulty of porting code that uses it is minimized. Its implementation seems to be as efficient as is possible without making the code unmaintainable. I seem to be the only user though.

I am satisfied with the features of Facade. It seems to do a good job of replacing the problematic processing part of AutoHotkey. Its set of useful combinators is better than most well-respected programming languages. The most useful feature by far is the error reporting.

I am slightly unsatisfied with Facade's interface. I would rather have one function per verb instead of one per verb per type. In other words, I would rather have generic functions. I have looked into ways to shoehorn this into AutoHotkey. It might be possible with a combination of interfaces on the types and dispatching to handle built-ins that lack the necessary interfaces. Good programming languages (e.g. Scheme) exist without generic functions though, and no other AutoHotkey library that I am aware of has attempted to force the matter on AutoHotkey.

There are some advantages to leaving Facade's interface as it is. For example, if you know the type that is expected, the verb is concatenate, and there are no arguments, you can return an empty instance of that type. A generic function would have to make that a defect because it could not know what type it was expected to return. The current interface also intentionally omits some operations that would be inefficient, which has the benefit of guiding the programmer to use appropriate types. An implementation built atop standard interfaces is unlikely to do that.

I am very unsatisfied with Facade's implementation.

The code structure is terrible.

This is the result of several forces:
  • I did not know what I needed to write because the problems AutoHotkey causes were not familiar to me (or most programmers). I could not just go read a tutorial.
  • I needed to implement features in a library in the target programming language that are normally a part of a programming language's runtime system or interpreter (e.g. primitive types and error reporting).
  • I needed to keep the code as simple and modular as possible because I was writing it in a programming language where even the operators are defective and it was actively working to hide defects from me. The only reasonable way to cope was to write a small part and test that part thoroughly before moving on to the next part.
  • I decided to follow the, usually good, practice of avoiding circular dependencies. I did not have much choice. One has to build the foundation before what rests upon it.
  • I wanted it to be at least somewhat time and space efficient.
  • AutoHotkey does not allow libraries to control what they import or export and ties function names to its import mechanism.
The need to implement primitive types is why the library containing the Dict type is separate from the Dict library. Also, several other libraries (like the Array library) use the Dict type.

The lack of import and export control and AutoHotkey's import mechanism is why some libraries and functions have funny names starting with _.

Trying to avoid code bloat (because, again, writing anything in a programming language that is both error-prone and hides errors is extremely difficult) and the lack of mixins is why the _Sinks.ahk library exists.

Knowing what I know now, I could probably structure it better, but it is not clear to me what the community would prefer for file management and efficiency trade-offs (e.g. I could make almost every type and function into its own library which would keep include bloat minimal but maximize the number of files, I could put it all in one file which makes the opposite trade-off, or I could do something in-between) or how they would prefer to cope with the unavoidable namespace pollution. That is one of those useful feedback problems.

Instead of having a library like _Sinks.ahk for code that is shared but not tied to the type hierarchy, I could monkey patch the classes themselves. It is unclear to me which gross option is least gross.

Facade is also somewhat slow. "Slow relative to what?", is a fair question. I mean slow relative to what equivalent code would be in most programming languages.

I made an optimization pass over Facade to apply what I have learned over the years to it. I will make a separate post about that because it might be of interest to people who have no interest in Facade.

One thing that remains very slow is recognizing arrays as such. Arrays with missing elements are unacceptable because they break most algorithms that would use an array (like reversing and sorting). Lua made the same mistake of conflating arrays and dictionaries but it explicitly forbids arrays with missing elements for this reason. AutoHotkey requires it (for optional argument behavior). So we effectively have to have a special array type for function application (where missing elements are tolerated) and have an 'actual array' type that must be used everywhere else. Since both are really the same type, the only reliable way to detect that something is an actual array is to check for the presence of every key-value pair (and before someone tries to contradict me, no, checking the count is not enough because non-1-to-length keys are possible, and no, comparing the count to the length is not enough for the same reason (e.g. 1, "foo", 3 keys)).

But a few months ago, by accident, I discovered that it is possible to define a type that built-ins seem to always consider an array so long as the interface (which they seem to mostly avoid using) is preserved. Specifically, any object with 1-to-length keys will do.

I could implement a GoodArray type and convert the rest of Facade to using it, but that would be a lot of work. I need convincing to exert the effort.

Autohotkey v2, despite being half-baked, seems to be nearing release, which might make all of this irrelevant.

Why the half-baked quip?

Several things I brought to the community's attention are still broken:
  • AutoHotkey is still possibly the only programming language in existence where whitespace carries a performance penalty (referring to the , operator)
  • the security obliterating practice of calling eval willy-nilly with directionless % brackets is still being promoted to novices, but at least experienced programmers can now avoid most uses of it
  • AutoHotkey is still weakly typed (e.g. Strings and Numbers coerce to each other)
  • mutable state still abounds in a 'multithreaded' environment
  • there is still no way to control what you import or export
  • mixins are still unsupported
  • there is still no unique null value, causing the semipredicate problem
  • there is still no IsString(Value) function
  • standard interfaces most programming languages would have (e.g. for operator overloading) are still unsupported
  • the shifts still break (but now throw exceptions) on ≥ word size shifts (as in x64 Assembly, but that is not an excuse because modern languages with bigints do not make this mistake), but that is a minor problem in the grand scheme of things
  • the floor division operator (in every other language) still performs truncating division (and now only works on integers)
  • Mod is still Rem
  • Round is still biased
  • Sort still sorts (and does other unrelated stuff to) a String instead of an Array
  • Arrays must still be able to have missing elements
  • 1-based indexing is still used
  • {} still constructs Objects instead of dictionaries (Maps)
  • the garbage collector still leaks cycles
  • there is still no REPL nor the infrastructure (e.g. a standard interface for string representation; be aware that converting a string to a string and converting a string to its representation returns different values (e.g. "foo" -> "foo" and "foo" -> """foo""" respectively)) to provide one
  • there is still no grid geometry manager, making GUI programming grueling
Some new breakage was included:
  • 0 ** 0 now throws an exception instead of returning 1
  • the logical operators do not return something that at least promotes to 0 or 1, preventing their use in bitwise operations and preventing branchless programming
  • the ability to seed the random number generator so that sequences can be regenerated was removed
  • a dictionary type now exists but is named Map, preventing the implementation of one of the most heavily used functional programming functions
  • the Map type still believes case-insensitive indexing should be directly supported (German, Greek, and Turkish exist; welcome to the dark future of Unicode)
  • the Map type still does not preserve insertion order, but that is a minor problem in the grand scheme of things
  • the Map type still mishandles floats (which is more difficult to code than handling them properly), but that is also a minor problem in the grand scheme of things
But it does report errors! That alone is a vast improvement!

In light of all that, I moved the fixed Mod and Round functions back to the Math library because it seems there will still be a need for a Math library if Facade is ported to v2. I also added some mathematical constants: Euler's number (often used with logarithms), phi (occasionally useful for things like Fibonacci hashing), and pi (often used in equations involving circles and angles) because I have needed them.

Most of the other changes to Facade were invisible to the user. Internally, it now uses a real type hierarchy (in _Validate.ahk) for error reporting which is slightly less verbose to use with the Type Checking library.
Last edited by [Shambles] on 15 Feb 2022, 11:39, edited 16 times in total.

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: gwarble and 46 guests