Feature Request: Multiple Subroutine Return Values

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.

I actually quite like the current design.

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)

1 Like

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”!

No thanks.

You think so ?

No need for code – just a JSON formatted return string.

1 Like

and a %JSONValue% token to reference particular components at the consuming end.

1 Like

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 :smiley:

I think perhaps you've misunderstood – the point has no connection with input parameters.

The point is that if you want, the single return value:

  1. Can be compound – expressed in the form of a JSON string (Array, Object), and
  2. particular components of it can be extracted with the use of a %JSONValue% token.

There's no need at all for multiple return values when the single return value can be compound anyway.

1 Like

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:
    • Right.tabName
    • Right.tabURL

SubRoutineAndCaller.kmmacros (9.1 KB)

compoundValue

1 Like

I think you're missing the point now!

KM 0 2022-03-31_22-57-03

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:

  1. Error Message
  2. Tab Name
  3. 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.

Anyway - it's all academic, right? :grinning:

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.

2 Likes

Hey Taj,

I knew more or less what Peter would say to this one from the get-go...

:sunglasses:

Think about the time and effort it would take to implement such a system...

Consider also how many normal users would complain about the added complexity...

Sure you can – change browserTab to browserTabJSON, and you can tell at a glance.

But – as Peter observed you have to manage JSON.

I would probably use a text-array with a custom separator instead.

-Chris

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)

You don't think so ?

InspectingSubroutineOutput.kmmacros (10.4 KB)

Screenshot 2022-04-01 at 11.39.07

First off, thanks to everyone for their comments. I am sorry to hear that my request won't be going anywhere :slightly_frowning_face:

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):

KM 0 2022-04-01_11-27-19

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:

KM 1 2022-04-01_11-27-19

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.

Anyway - as I said previously, it's all academic :smiley:

2 Likes

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.

3 Likes

tiffle,

image

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.

2 Likes

Thanks for making the effort @Zabobon! Your approach could certainly help and you've definitely given me something to think about. Cheers :grinning:

2 Likes

Based on the information shared above in this thread, here's an approach I've mocked up to return multiple values from a macro subroutine.

Please comment if you have any ideas for improvement!



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.


Image: caller—Example


Image: sub—ExampleReturningValuesUsingJSON

1 Like

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?

But I am probably missing something!

1 Like