The Subroutine feature introduced in Keyboard Maestro version 10 is a much improved way of encapsulating generally useful and reusable functionality than was previously available via the Execute a Macro action.
In general programming terms (and I simplify here just for the sake of clarity), subroutines and functions are similar but different: a function always returns a value while a subroutine can return zero or more values.
By this token subroutines as implemented in Keyboard Maestro version 10 are somewhat limited insofar as they are able to return zero or only one value.
The feature request to @peternlewis is, therefore, the ability to create subroutines that can return as many or as few values as desired and specified in much the same way as multiple input parameters are in the current implementation.
A single return value keeps things clean – simpler to compose and enchain – and if you want, you can always define it as a compound value (a JSON Object with key:value pairs, or an ordered JSON Array)
Sure - but that means extra code in the calling macro - code that needs to be duplicated in every calling macro every time the subroutine is used. That’s really the opposite of “keeping things clean to me”!
Well, when I create an Execute Subroutine action I can see the inputs explicitly. With your suggestion, I would have to look at the subroutine itself to understand the JSON being passed back in order to access its elements whereas my feature request would (hopefully) show those outputs (at least their names) explicitly thereby obviating the need to go to the subroutine to understand what’s being returned.
I’m just glad the current implementation doesn’t follow your model and expect a compound JSON value representing the input parameters.
I’m sure you’ll correct me if I’m missing your point
Hi - yes I realise that. I was just trying to say that I’d prefer a standardised method of specifying output from a subroutine. When I share subroutines I have created with fellow workers I would prefer it if the interfaces were self-documenting, which is basically how it already is on the input side. On the output side, however, it is currently left to the ingenuity of the subroutine-author as you are ably demonstrating with your suggestion!
The key names of a JSON object are as self-documenting as any other names.
(And JSON is more flexible – names can be nested too – no need for anything rigidly flat)
Here, for example (passing a browser bundle id to a subroutine, to harvest either an error message if there is no open tab, or the name and url of the front browser tab, if there is one):
any error message is called Left
and any successfully obtained value is called Right
In the absence of Left, Right is subdivided here into two distinct values:
browserBundleID is just the name of the variable in your subroutine and its purpose is self-evident.
However, I can tell that browserTab is the output of the subroutine, but what I can't immediately see is that it is a JSON construct whose interpretation is far from obvious until I see the written documentation to go with the subroutine. Indeed, you show how flexible it is but you do not show how any user of your subroutine can "decode" the output. In other words, to the end-user it is anything but self-documenting.
The feature I'm requesting for this specific example would be the ability to specify three outputs with the following labelling:
Error Message
Tab Name
URL
which informs the end-user exactly what is coming back from the subroutine.
My feature request of course would not prevent you using your approach - one output of a JSON object. In fact another user might prefer to output a single KM array but that too would require explicit documentation of what the array elements represent.
I think you are overly hopeful if you think there is consensus on the different meanings of function and subroutine. Very few languages support returning more than one value from any kind of function or subroutine, and many languages conflate the terms subroutine and function, and/or use function to include not returning an argument.
In any event, having multiple return values was a clear potential feature when I implemented the feature. I chose not to do it for simplicity. And I probably wont change that going forward.
There are clear downsides with having to return in in some sort of structure, such as JSON, since the value also has to be encoded to ensure the JSON is valid. But unfortunately, that is just what you'll need to do. In the odd case you want to return multiple values, I suggest you just relent and use Instance variables - we are not building operating systems here, structural programming methodologies do not need to be rigorously followed.
tiffle,
in my opinion, I didn't think that it is good choice to have multiple return values explicitly support by KM since as other commenters indicated that multiple return values can return as array or object json string and then can be destructured into variable name by KM JSON action.
and beside that KM Subroutine action does follow this standard function structure.
function funcName(param1, param2): returnValue { // Explicit Input
return returnValue; // Implicit output since it can collection of data (eg array, map ) , or literal data (eg string ,number)
}
What I do is to put output variable names in comment area. ( of course, there may be potential of variable name collision but it something that we can just fix by renaming the conflicting variables in the calling macro)
First off, thanks to everyone for their comments. I am sorry to hear that my request won't be going anywhere
I am also frustrated by my inability to get my point across to everyone but Peter! So here's my last shot at explaining what I'm trying to get at.
Imagine I'm putting together a macro that calls the above subroutine, as provided by @ComplexPoint. This is what I'll see in the KM editor (please note @macdevign_mac there's no explanatory comment):
From my perspective I might be able to deduce from the name of the subroutine that it returns 2 values. Those values might be held in a KM Array, or a JSON object; or whoever wrote the subroutine might have decided to return the values in a file or a named clipboard or even some instance variables. To understand how to get at the returned values I'd need to refer back to the actual code of the subroutine. This might be difficult to do - depending on the thoroughness of the author of the subroutine!
In contrast to this, have a look at what you'd possibly get if my feature request was implemented:
It is more obvious, to me at least, what it is the subroutine is giving back and more straightforward to make use of the subroutine itself.
If you never write subroutines that are going to be used by anyone else, then none of this really matters, but if you do it might save you a lot of time when users ask for help.
Hi @tiffle I can totally see your point, in terms of making it clear what is going on. As @peternlewis has indicated this is not going to be implemented but that Instance Variables could be used to return multiple values from the Subroutine, I had a play and made a Favorite Action like this which at least lets the end user know what is going on.
The comment area (aka the description header) I refer is at the Group action since the local variables created are not from Execute Subroutine (it just returns JSON) but from Set Variable with Prefix action from JSON (I use the description header rather than Comment Action or Note due to brevity and convenience). I group them because they are linked together.
DOWNLOAD Macro Files: Subroutine Examples Macros.kmmacros (10 KB) Note: These macros were uploaded in a DISABLED state. They must be ENABLED before they can be run. If they do not trigger, the macro group might also need to be ENABLED.
Hi @_jims
My instant thought (probably wrong!) is that this seems quite complicated.
Since you are saving stuff to Dictionaries why does the Subroutine need to return anything at all?
Can’t the Caller Macro simply access those Dictionary Values directly and then delete the Dictionary if you want to clear it out?
Or you could use multiple Instance Variables. They can be returned by any Subroutine without any special settings and clear themselves at the end of the run?