@peternlewis - Peter, I’ve been making a couple of assumptions for quite a while, and although I can’t imagine I’m wrong, I’d like to verify, and perhaps get it documented if it isn’t already:
ASSUMPTIONS:
A normal KM variable is Global in scope. It shares its content with all running macros that reference it. So a change to its value in one macro affects all macros.
Tokens like %Result Button% are Local in scope. A change to its value in one macro does not affect other running macros.
I mean, obviously I’m right or a lot of things wouldn’t work as expected. But still, you know what they say about assumptions.
So, of course, this begs the next question. Consider this scenario:
Macro A calls SubMacro, which returns the value “XX” in the variable SubMacroResult.
Macro B calls SubMacro, which returns the value “YY” in the variable SubMacroResult.
Macro A references the value of SubMacroResult, and gets “YY”.
OK, so the timing of that would have to be pretty specific to happen, but it could.
So let’s consider this more likely example:
Macro A calls SubMacro, which returns the value “XX” in the variable SubMacroResult.
Macro A references SubMacroResult, and gets “XX” (no problem).
Macro A prompts the user for something, and waits for the user to close the prompt.
Macro B calls SubMacro, which returns the value “YY” in the variable SubMacroResult.
(Macro B does not interact with the user, so you probably didn’t even notice it ran.)
The user closes Macro A’s prompt, and again references SubMacroResult. Only this time, it gets “YY”.
I realize the above situation could happen without a SubMacro, when a macro runs multiple times concurrently, but this is probably not a common situation, and could be prevented with a semaphore if need be.
Well, crap. I just realized that that leads me to the possibility that a SubMacro could run concurrently because it’s called from multiple running macros. Which means even the variables used locally in the SubMacro could be affected. Again, this might be helped by using semaphores.
So my questions are:
A) How much should I worry about this, and how should it affect how I design sub-macros? This is probably a question that can only be answered for myself, but it’s still a valid question.
###B) And here’s the kicker, the crux of the matter:
Any chance to add a feature to support variables that are local in scope to the executing macro chain, similar to Thread Local Storage?
Any chance to add a feature to support variables that are local in scope to an individual macro, i.e. their value is not available outside that macro?
As for implementation, my thought is a naming convention. This is not a new precedent, because “Prompt for User Input” treats variables with names containing double-underscores differently.
So, what do you think? Am I overthinking things? That’s my nature as an engineer at heart. “But what if…” is my middle name.
Keyboard Maestro variables are global in scope, yes. There is no concept of local variables currently.
There is no %ResultButton% token. There is a Result Button variable, and it is global in scope.
The tokens in the Macro Information category (currently, except the MacroNameForUUID token), are scoped to the execution thread.
Yes, this would be "bad". Not quite in the Ghostbusters sense, but getting up there.
Correct.
With the complexity of the macros you write, you should be worrying about this issue.
It is on the todo list. In fact, I implemented it to a degree, I just was not happy with the resulting complexity & confusion.
Anything is possible. The question is what it would look like.
Yes, that is probably what I would do. It becomes more tricky when you consider it in conjunction with the Variables preferences pane, AppleScript, debugging, etc. How do you debug something if you cannot see the value of the variables?
Perhaps. But probably only for things non-UI related. If a macro is triggered by some sort of user interaction, I would think most of the time it will finish before something similar comes along.
The issues you bring up with local variables are more daunting than I originally thought. I'm not worried about debugging and inspecting values, that's what they make "print" statements for.
But passing local variables to scripts, and setting local variables from scripts, is problematic to say the least, if not downright insurmountable.
So that leaves me with this thought for now:
At the moment, I think the easiest solution is to use semaphores in any situations where I think there's a high likelihood that simultaneous access might happen.
And I think a practice of naming variables so they're not likely to be used in other macros would help also, which is something I already do by using prefixes.
I don't like the system, the language, to use variable naming convention to establish any variable property, whether it be variable type or scope.
I would much prefer, and believe it is more clear and less error prone, to add a variable property to the Set Variable Action to check if that variable's scope is to be local to that Macro.
In fact, in interest of clarity, I would require these "local" variables to be set using a Set Variable Action PRIOR to use in any other Action.
I guess one issue is what about persistence? Do local variables persist like the standard, global variables?
I haven't given this a lot of thought, but my initial response is, no, local variables would not persist, and would be automatically deleted when the macro terminates.
If you need a local variable to persist (which I think would be rare), then set a global variable to it.
Otherwise, things get too complicated, both on the developer's side and the user's side of Keyboard Maestro.
Isn't that the same issue in many languages, like JavaScript?
IOW, it is up to the developer to properly declare his/her variables.
While I don't want the language to use a naming convention, I often have.
So any variable I declare local, I have used a prefix of "l" or "l_" (that's a lower case L). This helps to remind me.
The other approach, that I have taken with AppleScript, is to use a naming convention for script properties (which are global, and persist between executions), like "pty", and for global variables, like"g".
Here's an AppleScript example:
global gX
set gX to 1000
set localX to 5
set y to my testLocal(100)
log localX
-->5
log gX
-->2000
on testLocal(pVar)
set localX to 10 -- the var "localX" in the main script is not changed
set gX to 2000 -- the var "gX" in the main script IS changed
return pVar + 1
end testLocal