SubPatches as subroutines
Hi all
I find the "patcher" object very handy to create a function using various parameters as inputs. I was initially assuming that I could "call" this subpatch from various points in the main patch but found out that each "patcher" call creates its own (sub-)patch, even when using the same name as an argument. While I don't understand why it should be that way (we could use different names), I am still looking for a solution to call the same sub-patch from different places in the calling patch. Like using "send" and "receive" objects, but with parameters.
Any hint ?
Hi, i understeand what you mean. One way could be to use a send/receive pair and send a caller id to be able to assign the result to the caller. Click the message boxes in this example:
Another way is to create a .maxpat file and use it multiple times. You simply write the subpatcher name into an object box (subpatcher needs to exist in the searchpath). That will not increase the patcher size like if you duplicate the same [p xxx] abstraction multiple times. You can even specify parameters (in the object box) that will configure the same subpatcher to behave differently. Inside you refer to the parameters with a hashtag+number. Tell me if you need an example for this.
Thank you for the answer @11Olsen
:)
I am not so much concerned to return a value. So I believe your second suggestion is the good one. Yes a simple example with arguments (parameters) would definitely help ! (how to specify arguments, how to retrieve them in the subpatch,...).
Sorry if it sounds trivial (and I look stupid), but the MAX way of "programming" is new to me, albeit very interesting...
Thanks again.
I agree. Not having a simple way to define functions and subroutines is a bit of a handicap when you've used other languages before. I have put in a wish list for that with support. But I like the sound of @11Olsen's idea (he's full of good ideas!).
Here's an incredibly banal example of a subroutine (unimaginatively called subroutine.maxpat) that adds and multiplies two numbers:

@Andy Maskell Yeah I still don't understand the use for different patchers with the same name...
A "fix" would be to just use the same patch given a "patcher" name... If you need them to be different, just use different names !
Exactly! And it's odd given that for just about everything else that is given a "name" in Max (e.g. coll, text, send, receive, etc.) the instance is global.
I'm not sure anymore if i understand exactly. If you mean how to use parameters with an abstraction then you simply use a list that is unpacked inside or just multiple inlets:
The other thing is using the same subpatcher with different args: Put this subpatcher into your searchpath
Now look at this example
Depending on the args the same subpatcher works differently. Is that what you mean?
What I mean is exactly what @Andy said in his example. I guess my question is then:
How do you create the "Subroutine" patch ? I tried to create a new patch (File/New Patcher), save it to give it a name, and then call it (create object and write "Subroutine" in it or whatever name I used), but there is no link between the 2... For instance, in the calling patch, the Subroutine object does not show inputs and outputs corresponding to the inlets/outlets defined in the Subroutine patch...
:(
@Andy Hey i've seen multiple cases now where you use pipe or delay to set a specific order of the program flow. In most cases that's not necessary and also one has to be aware that it moves the output to the high priority (timer) thread. We have [trigger] and right2left order to take care of that. Just a hint mate.
@DR MAD I guess I need an example from you then. If the saved subpatch has inlet and outlet objects it will reflect that when you create it.?
@DR MAD subpatch needs to be saved in the searchpath otherwise object box looks like this

and console outputs

Forget what I said in my previous post... I forgot to save again the new patch after my changes... duh...
It does work now. Thank you very much.
I also wondered if the delay was necessary, and whether this did cause 2 outputs: one when the first parameter arrives, and one after the delay ?
delay makes no sense here and it's "wasting" 1ms before you get the output

Hi, maybe folks already understand this, but in case not: In many ways a subpatch is a subroutine: as long as you call into it from one message and don't have defers or delays in there it will process as a subroutine. Under the hood it's a call stack and all calls in your subpatch will complete before the calling object (the parent) continues it's call chain. So if you want it to behave deterministically like a subroutine, you just call it by sending one message to the inlet, and make that message a list that you break up in the subpatch. This can be done from multiple places with send and receive objects. Of course it doesn't *return* values, but that's how Max works - it's a dataflow language where all values proceed to the next call in the graph. An incoming message calls functions inside the receiving object, and when they complete, they send new messages that call functions in subsequent objects. Flow doesn't end from one call until that chain (which is a call stack) ends by hitting a function in an object that doesn't initiate other messages. So under the hood, you are indeed calling functions through the whole data flow graph. The main point of confusion is people wanting to make it return values, when fundamentally the patcher is data flow. You can think of it as always being functions that return void (nothing), but can call other functions. If you want to simulate a return, you need to send a message back to the parent somewhere, and have that flow *end* (with some kind of storage), and then have your call chain eventually get to that stored value. I guess you could also have the originator send some kind of id that gets used to update a receive object used to route back to a specific originator, but it's always going to be a bit complex that way. Matt Wright discusses this in his Kadenze course if you're interested.
FWIW, I do it in Scheme for Max by using scripting names and then it's easy to send results back where I want by sending a message to a named object from Scheme: (send 'my-receiver 'set 'foobar 1 2 3) etc. If you really want to get values back in a return it's probably easiest to do it in a text language in Max (JS, Scheme, C, etc)
You definitely can't have a delay or defer though or it instantly becomes asynchronous! Then you have something that works more like a network call with an async callback.
@11Olsen, yes you're right! I keep forgetting! It's a bad habit that I developed based on the original patch that I "learnt" Max from. I've changed my project to reflect that now. I just keep forgetting when I write something quickly.
@DrMad, the bang and delay was in there to force output from the expr statements when the righthand number is changed. @11Olsen's method is much the better way to do it.
Exactly! And it's odd given that for just about everything else that is given a "name" in Max (e.g. coll, text, send, receive, etc.) the instance is global.
And there's [value]. So even if subpatchers are separate instances you can always access something global inside of them.
Does anyone happen to know if you can get a value from a value object through the SDK directly? (ie, the way you can with dicts and buffers?)
@IAIN probably a new question for the dev section. You can find it like a named send/receive but I don't know how to get the actual value then.
Thank you all for very helpful answers!
:)