Feature Request: Multiple Subroutine Return Values

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

Hi, @Zabobon. Thanks for weighing in.

You're right, but the actions at the end of the subroutine and beginning of the caller would remain unchanged, notwithstanding the comment. (I placed these template actions in groups.)

However...

Well my thought was to come up with a method that would function properly if a subroutine was being concurrently used by multiple callers. But now that I think about it, since dictionaries are global, the approach I proposed above would fail. Here's a revision that would work assuming the callers specify unique dictionary names:


DOWNLOAD Macro Files:
Subroutine Examples Macros.kmmacros (9.2 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 v2


Image: sub—ExampleReturningValuesUsingADictionary


Yes, and if a caller only calls a subroutine once, then this approach is undoubtedly the simplest way to go. (I think @peternlewis made the same suggestion.) And even in cases when a caller makes multiple calls to a subroutine, values can be segregated if the instance variables are cleared prior to calls 2 to n:



DOWNLOAD Macro Files:
Subroutine Examples Macros.kmmacros (13 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 v3


Image: sub—ExampleReturningValuesUsingInstanceVariables

1 Like

In fact you don't even have to clear the instance variables as that will happen each time the Subroutine is run and replaces the values with new values. So, in my quick testing you don't need these Actions:

Click to show image

Sorry, I should have been more clear.

If the subroutine is trivial like with my example, it wouldn’t be necessary to clear the instance variables. But suppose it’s more complex and it includes conditional logic that caused cases where the setting of an instance variable is skipped. In these cases, there would be no way to fully inform the caller unless the caller effectively initializes the variable before calls 2 to n. (Since variables values are null before use, the manual clearing is unnecessary for call 1.)

On a related note, with the approach I used above in v2, the clearing is done when the dictionary is set to JSON {}.

After rereading this thread, experimenting with various options, and considering @Zabobon's comments, I think the below is the approach I will use to return multiple values from a subroutine. As I see it, here are the advantages:

  • Syntax for subroutine and caller is relatively simple.
  • Instance variables are not required.
  • Subroutine can be concurrently called by multiple callers with no risk of cross-contaminating values.
  • Even in cases when the caller makes multiple calls to the subroutine, it's easy to differentiate the returned values in the caller.

Comments still welcome. :grinning:



DOWNLOAD Macro Files:
Subroutine Examples Macros.kmmacros (14 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 v4


Image: sub—ExampleReturningValuesUsingJSON