PSA: When working inside some FUNCTIONS, you must use the % notation to reference variables, not just the variable name itself

TL;TR:
When passing a VARIABLE as a parameter to FUNCTION that take text (LINES(), CHARACTERS() and WORDS()), you MUST use the % Notation to reference variables: LINES(%Variable%MyText%)

Hi everyone,
I wanted to share a quick tip that might save you some troubleshooting headaches. While working on a client's macro, I ran into a common point of confusion regarding how variables are handled inside some text functions (even if you are inside calculations).

THE COMMON RULE WE ALL FOLLOW:
The Keyboard Maestro documentation tell us that when using a variable in a calculation, we should enter its name without the %Variable% token syntax.
For example, we use MyVariable * 10, not %Variable%MyVariable% * 10
image

THE CRITICAL EXCEPTION:
However, when working inside the some text FUNCTIONS (LINES, CHARACTERS, WORDS), you must use the % NOTATION to reference variables, NOT just the variable name itself.

To make this crystal clear, I've put together a demonstration (see screenshot and attached macro below).

Reference Variables inside Functions.kmmacros (3.7 KB)

Let's break down what's happening in the screenshot:

  1. The SETUP: We start by creating a variable called textLines containing three lines of text.

  2. The INCORRECT METHOD (Red Section) [Test Case 1]:

  • We try to count the lines using LINES(textLines).

  • Result: 1 . This is wrong. Keyboard Maestro doesn't recognize "textLines" as a variable here. It likely treats it as a literal string "textLines", which has only one line.

  • This happens in both the Set Variable to Calculation action and when using the %Calculate% token.

  1. The CORRECT METHOD (Green Section) [Test Case 2]:
  • We try again, but this time we use the full token syntax: LINES(%Variable%textLines%). BTW, if your variable name isn't a reserved word, you can simply use its abbreviation: LINES(%textLines%). But I suggest you use the full syntax.

  • Result: 3 . This is correct!

  • The reason this works is that Keyboard Maestro processes the %Variable%textLines% token first, replacing it with its actual multi-line content. Then, the LINES() function receives the correct data to work on.

I hope this clarification saves you time!

1 Like

This is not entirely correct I'm afraid.

Inside calculation fields, you can use variables unadorned.

And inside most functions, you can use variables unadorned. For example:

MAX(varx,vary).

Some functions has special coded parameters, eg SCREEN(Internal,Left). Internal and Left are what might be consider keywords/reserved words. They have special meaning, but only in those locations. You can also have SCREEN(varx,Left). In this case, the first field is numeric, and so again, variables are unadorned.

There are a few functions that instead take text parameters. There are not many of these, but some examples are LINES(Hello\nThere) and CALCULATE(1+2+3). In these functions, since they take text, you can use tokens, and one such token is the Variable token, so you can do LINES(%Variable%varx%).

But in general, function parameters are much more commonly numeric and thus variables require no adornment.

So your post only applies to a few Functions that take text parameters. I think the only ones are CALCULATE and the text size functions, CHARACTERS, WORDS and LINES, but it's possible there are a few others I forgot about.

4 Likes

Thanks for the clarification, Peter. I’ve worked with SCREEN and other functions, and you’re right, they do accept variables without adornments. There are a few problems I’ve noticed with SCREEN "mouse" calculations (unrelated to this thread), and I’ll try to post about them later this week to get your input on resolving them.

Since the documentation doesn’t clearly specify this behavior for CALCULATE and text size FUNCTIONS, I often get troubleshooting requests related to those, especially from developer and marketing teams I’ve helped transition to Keyboard Maestro.

It does say in the very first paragraph that:

The CALCULATE(t) function takes an arbitrary token text string, processes the text tokens, and then calculates value of the resulting numeric expression.

Similarly for the others you mention. And the examples, showing the use of both literal text and text tokens, should also help.

I'm not saying it's always obvious what to do -- I'm forever getting the result 1 for LINES() because I put the literal variable name in and not the token -- but it is documented.

1 Like

That’s why I emphasized the need to “clearly specify”, especially since the documentation contains multiple instances advising against using variables with adornments in Calculation actions or tokens:

When I asked someone from the marketing team to review that section of the documentation, their feedback was that it wasn’t explicit enough and lacked concrete examples. They pointed out that this made it harder to reconcile with conflicting instructions found elsewhere.

Keyboard Maestro is known for having some of the best documentation in the productivity and automation space, for both regular and advanced users. That’s why it always surprise me when clients from development, QA and marketing teams tell me that their macros aren't working as expected, even after I encouraged them to consult the documentation and forums.

In this particular case, they sent me screenshots of both the documentation and their macros, which made it clear that this issue was worth highlighting.

You can't "clearly specify" how to enter every Function on the "Set Variable to Calculation" page -- your marketing team would throw up their hands in horror!

What conflicting instructions? It sounds more like they are conflating the %Calculate% text token with the CALCULATE() function, and not understanding that "text" and "numeric" contexts are present within tokens/functions just as they are in fields -- and that those contexts nest:

Local_myVar + CALCULATE(%Calculate%Local_myVar + 1 + CALCULATE(%Variable%Local_myVar%1)%)
                                                               <text (from CALCULATE)>
                                   <-------------numeric (from %Calculate%)----------->
                        <---------------------text (from CALCULATE)-------------------->
<--------------------------------numeric (from the field)------------------------------->

Again, I'm not saying this is obvious. Just that the complexities can't be documented each and every time they crop up, for each and every possible combination.

But the basic "rules" are documented -- and if they understand those then they'll be whipping out macros way quicker than I wrote this!

Given that the % notation IS REQUIRED to reference variables in the Text Functions LINES, CHARACTERS, and WORDS, I do think it's worth explicitly documenting that. Even if the full complexity can't always be covered, a clear note on this specific requirement would help avoid confusion for newcomers.

The main source of confusion for newcomers seems to be the wording in the documentation for LINES, CHARACTERS, and WORDS. While their descriptions are similar to other Text Functions like CALCULATE, their behavior differs in an important way.

For example:

CALCULATE(t) function:
Takes an arbitrary token text string, processes the text tokens, and then calculates the value of the resulting numeric expression.

CHARACTERS(t) function:
Takes an arbitrary token text string, processes the text tokens, and then returns the number of characters in the string.

However, unlike CALCULATE, the CHARACTERS, LINES, and WORDS functions do not process a variable name unless it is explicitly referenced using the % notation. This difference isn't immediately obvious from the wording alone.

I understand that the key difference is that the CALCULATE function processes the text tokens and then attempts to interpret any resulting variable name as a numeric expression, unlike the other Text Functions.

A clear note in the documentation for LINES, CHARACTERS, and WORDS about the required use of % to reference variables would go a long way in reducing confusion for newcomers.

Again -- I don't think so... You've bolded the wrong bit:

CALCULATE(t) function:
Takes an arbitrary token text string, processes the text tokens, and then calculates the value of the resulting numeric expression.

So in

CALCULATE(initialNumber)

...the resulting "numeric expression" to be evaluated is initialNumber. In a KM "numeric expression" that is the correct form for variable access!

This:

image

...is the same as:

image

Whereas the "proper" form:

image

...is first evaluated to:

image

...and the string "1000" then evaluated as the numeric expression.

While it reminded me of type coercion in JavaScript and the issues that can arise from it, I believe CALCULATE() is functioning as expected for a "Text Function": it first PROCESSES the text tokens in the input, and THEN evaluates the expression as a calculation (similar to how a Calculation Action or calculation token would behave, by attempting to interpret any remaining string as a variable).

Case Process Tokens Resul of Calculation
CALCULATE(3*%Variable%initialNumber%) CALCULATE(3*1000) 3000
CALCULATE(3*initialNumber) CALCULATE(3*initialNumber) 3000

I believe Keyboard Maestro always tries to interpret any string entered in a numeric field as a variable name. Even if the variable's value is just a plain text string, it will still attempt to evaluate this new string as a variable name. Is this correct, @peternlewis ?

image

Are you willing to upload that macro so I don't have to retype it? (And because I'm not getting the same results as you.)

You mean the latest macro ("Strings in Numeric Fields")? If so, here it is:

Strings in Numeric Fields.kmmacros (3.0 KB)

image

I thought I knew everything about the Set actions, but your macro shows something new to me. Thanks for the example for me to ponder. I see Nige is going to reply so maybe he will explain.

Obviously not, because you've posted examples of LINE() etc where that is not happening.

I'm plainly not explaining this well, but I'll have another try...

Actions' fields have context -- textual (has a T symbol in it), numeric (N), variable (V).

  • To use a variable in a "text" field you use the Variable text token
  • To use a variable in a "numeric" or "variable" field you use the bare variable name

All the above is on the Variables Wiki page.

But tokens and functions also have their own internal context, which override the field context for the scope of that function/token. Going back to the CALCULATE() token:

image

...the "to:" field is "numeric". But the CALCULATE() parameter's context is "text". It does not contain any text tokens to "expand", it is just the literal string Local_result + 1.

But that literal string is also a numeric expression, as you can see in this valid "Calculation" action:

image

So CALCULATE() "calculates the value of the resulting numeric expression", just as the "Calculate" action's field does, and using the same variable-naming -- it evaluates the variable Local_result (1), adds 1 to it, returns the result (2).

Now that the function has completed, "replaced" with the number 2, you are back in the field's numeric context and the expression 2 * 2 is evaluated and Local_result is set to 4.

Perhaps pseudocode shows it better, since we can represent strings the usual way:

set Local_result to 1

2 * ( CALCULATE( "Local_result + 1" ) )
-- text is processed to a numeric expression, there are no tokens
2 * ( CALCULATE( Local_result + 1 ) )
-- CALCULATE's numeric expression is evaluated -- first the variable is resolved
2 * ( CALCULATE( 1 + 1 ) )
-- then the CALCULATE
2 * ( 2)
-- then the field is evaluated and the result returned
2 * 2
--> 4

And we can do the same for when we use the text token %Variable%Local_result% instead:

image

set Local_result to 1
2 * ( CALCULATE( "%Variable%Local_result% + 1" ) )
-- text token is processed
2 * ( CALCULATE( "1 + 1" ) )
-- CALCULATE's numeric expression is evaluated, no variable to resolve
2 * ( 2 )
-- then the field is evaluated and the result returned
2 * 2
--> 4

If you follow through the same logic for your third action in the macro above you'll see exactly why the result is 3000:

CALCULATE( " 3 * %Variable%nameOfAVariableAsSimpleText% " )
-- text token is processed
CALCULATE( " 3 * initialNumber ")
-- and the text treated as a numeric expression
CALCULATE( 3 * initialNumber )
-- CALCULATE's numeric expression is evaluated -- first the variable is resolved
CALCULATE( 3 * 1000 )
-- then the CALCULATE
( 3000 )
-- then the field is evaluated and the result returned
3000
--> 3000
1 Like

The third action makes sense, it's the fourth that I don't understand and was hoping you would explain. There seems to be a double dereferencing going on there, which is what is surprising me.

I think so too -- it looks like CALCULATE() recursively evaluates until a numeric expression is achieved?

Consider this:

CALCULATE Nested Variables.kmmacros (3.4 KB)

...which I'm guessing goes

CALCULATE( "Local_c" )
-- text processed to
CALCULATE( Local_c )
-- expression evaluated to
CALCULATE( "Local_b + 1" )
-- text processed to
CALCULATE( Local_b + 1 )
-- expression evaluated to
CALCULATE( "Local_a + 1" + 1 )
-- text processed to
CALCULATE( Local_a + 1 + 1 )
-- expression evaluated to
CALCULATE( 1 + 1 + 1 )
--> 3

Perhaps best kept in reserve for the "Obfuscated Macro" awards? :wink:

I don't think it's just the CALCULATE() function, rather, calculations in general seems to evaluate as a variable name the entire text string as a whole first, before attempting to parse and resolve individual variable segments (see Text case 5):

Strings in Numeric Fields Test cases.kmmacros (11.0 KB)

image

I like your humour, but I was thinking more along the lines of possible tricks I can use in the future to solve new problems.

Mostly.

In a calculation context (which would be a numeric field, or perhaps a location like within a Calculate token), a variable can be used unadorned.

When a variable is used in such a way, it needs to contact a numeric value. But variables are, as we all know, strings. So in fact that variable’s string context are evaluated as a numeric calculation. So if a variable contains “3+4” the variable will have a value of 7 for the purposes of the calculation. If a variable contains “3*varx” then that value will be the value of variable “varx” times 3.

If you are silly enough to make a variable depend on its own value (eg the variable varx contains a value of 3*varx), then the calculation will fail, just like it would if the variable contains text blarg.

Yes, because the variable needs a numeric value. So there is a clearly defined meaning. The only alternative would be to fail the calculation (which at some point in the past is what would have happened).

Not really, more accurately it goes:

calculation input is `CALCULATE(Local_c)`
text process of `Local_c` is `Local_c`
calculation input of `Local_c` results in calculation of `Local_c`’s value
`Local_c` contains text `Local_b+1` so calculation of that is 1 more than the value of `Local_b`
`Local_b` contains text `Local_a+1` so calculation of that is 1 more than the value of `Local_a`
`Local_a` contains text `1` so calculation of that is 1.

The only text processing happening is the initial processing of the CALCULATE parameter “Local_c”. That is also the only place that tokens could appear. For example, if Local_a contained the text %ICUDateTime%yyyy%, the calculation would be invalid.

Don't get confused by this and think that if the first action was:

image

that wouldn't work. That would work fine, because that action itself would process the tokens and the value of the Local_a variable would be 2025 (this year). If you turn off token processing tho:

image

that would fail in the example above because %ICUDateTime%yyyy% is not a valid calculation.

3 Likes