How to apply oo within Max

Mattijs's icon

Hi all,

This one goes out to all object oriented programmers that use Max.

When patches get bigger, the need increases for a design principle that keeps you in charge. It would be great to construct a document/tutorial/convention that lays out the most efficient way to apply oo principles to programming in Max, if possible at all.

I am very interested to hear your thoughts about possible comparisons and how you use your oo experience in your patches.

Regards,
Mattijs

Mattijs's icon

I'll kick off with an attempt to an 'oo to max' glossary:

* class -> abstraction or subpatcher prototype;
* instance -> an instantiated abstraction or one copy of a subpatcher prototype;
* variable -> pv;
* access to a public variable from outside its scope -> impossible
* private method call -> subpatcher inlet;
* public method call with one argement -> pattr;
* scope -> subpatcher level;
* namespace -> pattrmarker;
* inheritance -> pattr bound to parent, pv;
* polymorphism -> subpatcher arguments;
* method with return -> grab with unique name (using #0)
* data type -> only 4 options: int, float, list, symbol
* return type -> irrelevant
* abstract class -> ..
* static method -> ..
* typecasting -> ..
* ..

Feel free to fill in the gaps, or remake the whole thing.

Cheers,
Mattijs

adamsynnott's icon

Are class constructors at all possible with max sub patching?

Mattijs's icon

Quote: adamsynnott wrote on Sun, 25 March 2007 00:10
----------------------------------------------------
> Are class constructors at all possible with max sub patching?
----------------------------------------------------

Yeah, like this, for example:

Max Patch
Copy patch and select New From Clipboard in Max.

Mattijs

Gregory Taylor's icon
Gregory Taylor's icon
Mattijs's icon

Quote: Gregory Taylor wrote on Sun, 25 March 2007 10:01
----------------------------------------------------
> http://jamoma.org/
>
>
> https://cycling74.com/forums/index.php?t=msg&goto=97531&rid=0&srch=Jamoma#msg_97531
>
>
----------------------------------------------------

Thanks for the input Gregory.

However it seems that both Arne's article and Jamoma do not address object oriented principles. Both approaches have an inside-out focus, i.e. working with the Max idiom and optimizing from there.

Although both are very valueable initiatives, I prefer to look at Max in the context of standard programming conventions. Computer programming has been here for a longer time than max patching and posesses a much higher manageability/complexity ratio. Whenever I have a decision to make about structure or organization in my patches, I first think about the way I would do it in Java or C++ and make my decisions from there. This way I am able to realize far more complex patches than, well.., others.

Cheers,
Mattijs

jln's icon
Anthony Palomba's icon

Agreed, JAMOMA is about standaridizing a Max api. OO is about
the the structuring and organizatrion of thought. It is a sciecne all
its own. I think OO Max would be a great idea. Being a software engineer,
I understand how OO design can help manage huge projects and
reduce the amount of owrk involved in buiding on existing projects.
It would only make sense that I would want to apply those skills to
my musical abstractions and systems. I think we should create some
examples that demonstarte these concepts.

Actually you can access to a public variable from outside its scope.
pattr not only allows me to address parent data, but child data as well.
Interestingly enough, pattr won't let me bind to data members that are
initialized with patcherargs. See examples below...

Anthony

Max Patch
Copy patch and select New From Clipboard in Max.

----- Original Message -----
From: "Mattijs Kneppers"
To:
Sent: Sunday, March 25, 2007 6:57 AM
Subject: [maxmsp] Re: How to apply oo within Max

>
> Quote: Gregory Taylor wrote on Sun, 25 March 2007 10:01
> ----------------------------------------------------
> > http://jamoma.org/
> >
> >
> >
https://cycling74.com/forums/index.php?t=msg&goto=97531&rid=0&srch=Jamoma
#msg_97531
> >
> >
> ----------------------------------------------------
>
>
> Thanks for the input Gregory.
>
> However it seems that both Arne's article and Jamoma do not address object
oriented principles. Both approaches have an inside-out focus, i.e. working
with the Max idiom and optimizing from there.
>
> Although both are very valueable initiatives, I prefer to look at Max in
the context of standard programming conventions. Computer programming has
been here for a longer time than max patching and posesses a much higher
manageability/complexity ratio. Whenever I have a decision to make about
structure or organization in my patches, I first think about the way I would
do it in Java or C++ and make my decisions from there. This way I am able to
realize far more complex patches than, well.., others.
>
> Cheers,
> Mattijs
>
>
>
>
> --
> SmadSteck - http://www.smadsteck.nl
> Interactive audiovisual sampling soft- and hardware
>

Mattijs's icon
Mattijs's icon
jvkr's icon

> That's right, patcherargs are triggered after all loadbangs, but in 'random' order. In fact the order is determined by the order in which you created the subpatchers. The last one you created comes first.

Not sure if it is the same with pattr, but generally speaking you can play with this order.

_
johan

Max Patch
Copy patch and select New From Clipboard in Max.


Mattijs's icon

Quote: jvkr wrote on Mon, 26 March 2007 13:42
----------------------------------------------------
>
> > That's right, patcherargs are triggered after all loadbangs, but in 'random' order. In fact the order is determined by the order in which you created the subpatchers. The last one you created comes first.
>
> Not sure if it is the same with pattr, but generally speaking you can play with this order.
>
> _
> johan
>
>
> #P window setfont "Sans Serif" 9.;
> #P window linecount 1;
> #P comment 170 202 100 196617 first is first;
> #P message 53 202 113 196617 script sendtoback first;
> #P message 39 166 119 196617 script bringtofront first;
> #N thispatcher;
> #Q end;
> #P newobj 39 232 61 196617 thispatcher;
> #P newex 387 214 32 196617 print;
> #P newex 441 139 84 196617 loadmess second;
> #P comment 159 166 100 196617 first is second;
> #P newex 345 135 75 196617 loadmess first;
> #P objectname first;
> #P connect 0 0 3 0;
> #P connect 2 0 3 0;
> #P connect 5 0 4 0;
> #P connect 6 0 4 0;
> #P window clipboard copycount 8;
>
----------------------------------------------------

Nice trick :) Not a solution for a modular environment, but still.

Mattijs

lists@lowfrequency.or's icon
Mattijs's icon

Quote: Anthony Palomba wrote on Mon, 26 March 2007 15:49
----------------------------------------------------
> Thats why I would say pv should be the OO equivalent of a
> private variable. "pattr @bindto" can access those data members
> that are public.

I agree, then pv should be the private variable. I think we don't really need a public variable. We do need a method that changes the value of the private variable. I agree that this could be pattr. I made a small example below, let me know what you think.

Mattijs

In fact a pattr might even act like a get and set method at the same time (not illustrated in the example).

Max Patch
Copy patch and select New From Clipboard in Max.


Mattijs's icon

Quote: lists@lowfrequency.or wrote on Mon, 26 March 2007 16:11
----------------------------------------------------
> for public variables, you have pattrmarker to create globally-
> accessible namespaces for patchers.
>

For a global namespace pattrmarker is a great candidate. It was in my glossary attempt earlier in this thread.

But for a public variable a global mechanism wouldn't work. We explicitly need variables to be local.

Cheers,
Mattijs

Stefan Tiedje's icon

Mattijs Kneppers schrieb:
> I first think about the way I would do it in Java or C++ and
> make my decisions from there. This way I am able to realize far more
> complex patches than, well.., others.

But maybe the solutions could be less complex if you would not restrict
yourself to "official" oo-thinking... ;-)

I learned programming, first as spaghetti with Fortran, then procedural
with Pascal and especially Modula II. Then there was Oberon, and there
where some interesting ancient discussions. Nicolas Wirth claimed that
Oberon (the successor to Modula II) is an object oriented language. The
interesting for me was, that he just showed how to translate those
"official" oo-talk into the concepts behind Oberon. And the Oberon way
of thinking is much more intuitive, as the Max way of thinking is (for
me at least ;-).
Many of the pictures which are used to explain oo-talk could be easily,
without need of explanation, be done in a signal flow language like Max.
The picture and the program are the same, as you can translate those
fancy flowcharts directly into working patches...

You already translated some oo-talk into Max-talk, and reading Arne's
article is easy to map to oo-thinking. You'd might make things more
complicated than they need to be eventually...

by the way your list could need an update with two lines:

* variable -> pv;
* access to a public variable from outside its scope -> impossible

to

* local variable -> pv; (as its local ther is no access from outside)
* global variable -> pattr, value, any named object;
* access to a public variable from outside its scope -> pattr, pattrforward;

I'd rather think of information flow between objects, than of objects...

Stefan

--
Stefan Tiedje------------x-------
--_____-----------|--------------
--(_|_ ----|-----|-----()-------
-- _|_)----|-----()--------------
----------()--------www.ccmix.com

Stefan Tiedje's icon

Mattijs Kneppers schrieb:
> When you want an object to be instatiated for example before it
> receives a loadbang from 'outside', you'll have to use a loadbang to
> trigger patcherargs, which is shady.

If you place this inside, you should be able to sort it...
Its only a workaround, but should be fine even if the behavior will
change in the future...

Max Patch
Copy patch and select New From Clipboard in Max.

--
Stefan Tiedje------------x-------
--_____-----------|--------------
--(_|_ ----|-----|-----()-------
-- _|_)----|-----()--------------
----------()--------www.ccmix.com

Stefan Tiedje's icon

Anthony Palomba schrieb:
> Interestingly enough, pattr won't let me bind to data members that are
> initialized with patcherargs. See examples below...

Wrong, you didn't name the subpatcher. It perfectly works here...

Stefan

--
Stefan Tiedje------------x-------
--_____-----------|--------------
--(_|_ ----|-----|-----()-------
-- _|_)----|-----()--------------
----------()--------www.ccmix.com

_j's icon

Quote: Stefan Tiedje wrote on Tue, 27 March 2007 01:56
----------------------------------------------------
> Anthony Palomba schrieb:
> > Interestingly enough, pattr won't let me bind to data members that are
> > initialized with patcherargs. See examples below...
>
> Wrong, you didn't name the subpatcher. It perfectly works here...
>
> Stefan
>
> --
> Stefan Tiedje------------x-------
> --_____-----------|--------------
> --(_|_ ----|-----|-----()-------
> -- _|_)----|-----()--------------
> ----------()--------www.ccmix.com
>
>
----------------------------------------------------

PWNED!! =)

Mattijs's icon

Quote: Stefan Tiedje wrote on Tue, 27 March 2007 10:40
----------------------------------------------------
> Mattijs Kneppers schrieb:
> > I first think about the way I would do it in Java or C++ and
> > make my decisions from there. This way I am able to realize far more
> > complex patches than, well.., others.
>
> But maybe the solutions could be less complex if you would not restrict
> yourself to "official" oo-thinking... ;-)

I think this is hard to determine if you don't know which level of complexity we're talking about. I can assure you that from a certain level patches are impossible to manage if you didn't work with very strict conventions and an overall structure. oo is one possible structure, but it happens to be one that is currently used by literally every major software company.

>
> I learned programming, first as spaghetti with Fortran, then procedural
> with Pascal and especially Modula II. Then there was Oberon, and there
> where some interesting ancient discussions. Nicolas Wirth claimed that
> Oberon (the successor to Modula II) is an object oriented language. The
> interesting for me was, that he just showed how to translate those
> "official" oo-talk into the concepts behind Oberon. And the Oberon way
> of thinking is much more intuitive, as the Max way of thinking is (for
> me at least ;-).
> Many of the pictures which are used to explain oo-talk could be easily,
> without need of explanation, be done in a signal flow language like Max.
> The picture and the program are the same, as you can translate those
> fancy flowcharts directly into working patches...

You seem to have some kind of grudge against oo, I can't imagine what it could be but perhaps it would be good to first get into it a bit more before rejecting it..?

>
> You already translated some oo-talk into Max-talk, and reading Arne's
> article is easy to map to oo-thinking. You'd might make things more
> complicated than they need to be eventually...

I really don't think Arne article has anything to do with oo. He talks about general optimization and patching principles, which will definitly help you when working towards more complex patches. But it's not a general template that lets you manage a big program. As he states in the beginning of his article, he's not a programmer.

Of course there is always the danger of making things more complicated than necessary (k.i.s.s.) but that's a separate issue. When programming a simple patch there is no need for any structure, needless to say oo. But when things get complex it's different.

>
> by the way your list could need an update with two lines:

Thanks for the input:

> * local variable -> pv; (as its local ther is no access from outside)

agreed (as stated earlier)

> * global variable -> pattr,

not agreed, I think the local character of pattr is much more useful

> value,

agreed

> any named object;

not agreed, see pattr

> * access to a public variable from outside its scope -> pattr, pattrforward;

See earlier posts. I vote for pattr to be more like a function.

Cheers,
Mattijs

Btw, your idea to derive an instance number out of the object name (where the index appears within [] and is automatically updated by max) is very valueable for this discussion. I think I will include the javascript we discussed at that time in the next example.

Mattijs's icon

Almost right. patcherargs outputs the attributes sequentially so the zl reg won't work. This one should:

Max Patch
Copy patch and select New From Clipboard in Max.

But in general, when triggering patcherargs with loadbang, another problem rises, see

Mattijs

Quote: Stefan Tiedje wrote on Tue, 27 March 2007 10:52
----------------------------------------------------
> Mattijs Kneppers schrieb:
> > When you want an object to be instatiated for example before it
> > receives a loadbang from 'outside', you'll have to use a loadbang to
> > trigger patcherargs, which is shady.
>
> If you place this inside, you should be able to sort it...
> Its only a workaround, but should be fine even if the behavior will
> change in the future...
>
> #P window setfont "Sans Serif" 9.;
> #P window linecount 1;
> #P newex 218 183 67 196617 zl reg;
> #P newex 134 184 38 196617 zl reg;
> #P newex 162 102 66 196617 patcherargs;
> #P newex 218 153 50 196617 onebang;
> #P newex 218 127 67 196617 route done;
> #P newex 162 75 50 196617 loadbang;
> #P fasten 2 0 4 0 223 176 139 176;
> #P connect 2 0 5 0;
> #P connect 1 1 5 1;
> #P connect 3 0 4 1;
> #P connect 0 0 3 0;
> #P fasten 0 0 2 1 167 97 167 97 263 97;
> #P connect 3 1 1 0;
> #P connect 1 0 2 0;
> #P window clipboard copycount 6;
>
>
>
>
> --
> Stefan Tiedje------------x-------
> --_____-----------|--------------
> --(_|_ ----|-----|-----()-------
> -- _|_)----|-----()--------------
> ----------()--------www.ccmix.com
>
>
----------------------------------------------------

Mattijs's icon

Ah you're right Stefan. I missed this one too.

Phew.. one less issue to worry about.

Mattijs

Quote: Stefan Tiedje wrote on Tue, 27 March 2007 10:56
----------------------------------------------------
> Anthony Palomba schrieb:
> > Interestingly enough, pattr won't let me bind to data members that are
> > initialized with patcherargs. See examples below...
>
> Wrong, you didn't name the subpatcher. It perfectly works here...
>
> Stefan
>
> --
> Stefan Tiedje------------x-------
> --_____-----------|--------------
> --(_|_ ----|-----|-----()-------
> -- _|_)----|-----()--------------
> ----------()--------www.ccmix.com
>
>
----------------------------------------------------

Anthony Palomba's icon

awww man, I got served! Indeed I did not name the subpatcher.
Here is the corrected version

Max Patch
Copy patch and select New From Clipboard in Max.

----- Original Message -----
From: jamez
Date: Tuesday, March 27, 2007 3:05 am
Subject: [maxmsp] Re: Re: How to apply oo within Max

>
> Quote: Stefan Tiedje wrote on Tue, 27 March 2007 01:56
> ----------------------------------------------------
> > Anthony Palomba schrieb:
> > > Interestingly enough, pattr won't let me bind to data members
> that are
> > > initialized with patcherargs. See examples below...
> >
> > Wrong, you didn't name the subpatcher. It perfectly works here...
> >
> > Stefan
> >
> > --
> > Stefan Tiedje------------x-------
> > --_____-----------|--------------
> > --(_|_ ----|-----|-----()-------
> > -- _|_)----|-----()--------------
> > ----------()--------www.ccmix.com
> >
> >
> ----------------------------------------------------
>
>
> PWNED!! =)
>

Stefan Tiedje's icon

Mattijs Kneppers schrieb:
> You seem to have some kind of grudge against oo, I can't imagine what
> it could be but perhaps it would be good to first get into it a bit
> more before rejecting it..?

You're absolutely right here... (I don't reject it, I am just jealous if
someone can talk oo... I have enough difficulties with French already... ;-)

>> * global variable -> pattr,
>
> not agreed, I think the local character of pattr is much more useful

But you can use it for both, its a matter of how you place them in
combination with pattrmarker.

> See earlier posts. I vote for pattr to be more like a function.

For me a function does something, pattr doesn't "do" anything but
storing, in my nomenclatura thats a variable and not a function...

Stefan

--
Stefan Tiedje------------x-------
--_____-----------|--------------
--(_|_ ----|-----|-----()-------
-- _|_)----|-----()--------------
----------()--------www.ccmix.com

Mattijs's icon

Quote: Stefan Tiedje wrote on Wed, 28 March 2007 00:10
----------------------------------------------------
> Mattijs Kneppers schrieb:
> > You seem to have some kind of grudge against oo, I can't imagine what
> > it could be but perhaps it would be good to first get into it a bit
> > more before rejecting it..?
>
> You're absolutely right here... (I don't reject it, I am just jealous if
> someone can talk oo... I have enough difficulties with French already... ;-)

Ah, mais vraiment, ce n'est pas trop difficile. If you accidentally program some Java now and then, you automatically run into the vocabulary. And without the language barrier the principles behind it make simple sense. Thanks by the way for coping with my incomplete english all this time ;)

>
> >> * global variable -> pattr,
> >
> > not agreed, I think the local character of pattr is much more useful
>
> But you can use it for both, its a matter of how you place them in
> combination with pattrmarker.

You are right about that. Anyways strictly speaking global variables aren't used in an oo program. Variables always belong to an object. But sometimes it can be practical to have them nonetheless ;)

>
> > See earlier posts. I vote for pattr to be more like a function.
>
> For me a function does something, pattr doesn't "do" anything but
> storing, in my nomenclatura thats a variable and not a function...

Indeed it's better to speak of pattr as the function -call-, not the body of the function. It stores data too, which is not necessary when used as a function call. But I know no other local object that can be triggered from outside a subpatcher but pattr. Have a look at the example I posted earlier to see how I was planning to use pattr as a function call.

Two more issues:

- there is still the problem of having a function return something. A local grab that works on a subpatcher would be good :))

- using pv for a private variable has one drawback; it can't be overridden :/

Any ideas?

Best,
Mattijs

Mattijs's icon
jln's icon
Anthony Palomba's icon

"there is still the problem of having a function return something. A
local grab that works on a subpatcher would be good"

This is something I have always wanted. It would be nice to be able
to have one instance of an object and be able to execute a subpatcher
in that object and get the result back. The imporant thing is that
it needs to be synchronous. It would be nice if one could use pattr
to trigger a subpatcher, passing any arguments as attributes.

Anthony

----- Original Message -----
From: Mattijs Kneppers
Date: Wednesday, March 28, 2007 2:44 am
Subject: [maxmsp] Re: Re: Re: How to apply oo within Max

>
> Quote: Stefan Tiedje wrote on Wed, 28 March 2007 00:10
> ----------------------------------------------------
> > Mattijs Kneppers schrieb:
> > > You seem to have some kind of grudge against oo, I can't
> imagine what
> > > it could be but perhaps it would be good to first get into it
> a bit
> > > more before rejecting it..?
> >
> > You're absolutely right here... (I don't reject it, I am just
> jealous if
> > someone can talk oo... I have enough difficulties with French
> already... ;-)
>
> Ah, mais vraiment, ce n'est pas trop difficile. If you
> accidentally program some Java now and then, you automatically run
> into the vocabulary. And without the language barrier the
> principles behind it make simple sense. Thanks by the way for
> coping with my incomplete english all this time ;)
>
> >
> > >> * global variable -> pattr,
> > >
> > > not agreed, I think the local character of pattr is much more
> useful>
> > But you can use it for both, its a matter of how you place them
> in
> > combination with pattrmarker.
>
> You are right about that. Anyways strictly speaking global
> variables aren't used in an oo program. Variables always belong to
> an object. But sometimes it can be practical to have them
> nonetheless ;)
>
> >
> > > See earlier posts. I vote for pattr to be more like a function.
> >
> > For me a function does something, pattr doesn't "do" anything
> but
> > storing, in my nomenclatura thats a variable and not a function...
>
> Indeed it's better to speak of pattr as the function -call-, not
> the body of the function. It stores data too, which is not
> necessary when used as a function call. But I know no other local
> object that can be triggered from outside a subpatcher but pattr.
> Have a look at the example I posted earlier to see how I was
> planning to use pattr as a function call.
>
>
> Two more issues:
>
> - there is still the problem of having a function return
> something. A local grab that works on a subpatcher would be good :))
>
> - using pv for a private variable has one drawback; it can't be
> overridden :/
>
> Any ideas?
>
> Best,
> Mattijs
>
> --
> SmadSteck - http://www.smadsteck.nl
> Interactive audiovisual sampling soft- and hardware
>
>

jln's icon
johnpitcairn's icon

Quote: Anthony Palomba wrote on Thu, 29 March 2007 05:05
----------------------------------------------------
> It would be
> nice to not have to use send and recv pairs.

I don't understand the "no send/receive" stuff. Sure, if you populate your patcher/abstraction (object) with lots of send/receive entry/exit points, that's asking for trouble, but what's wrong with scripting receives and sends (or forward) into place at instantiation, with unique send/receive names taken from an argument? Y'know:

[r myobject.input1] and [s myobject.output1]

How is sending a message to that myobject.input1 different from connecting to a patcher input and sending a message?

If listening for a message from the object's output send is problematic, and you need the object to explicitly message another named receive (which may change), then what's wrong with [forward], with the destination set on the fly, either as an argument to the input message, or as a "set output1" message (which could alternately delete/create the send if better performance is crucial)?

Connecting everything by patchcords strikes me as imposing considerable constraints on the app structure. Patchcords are certainly desirable in smaller structures to visualise the data flow, but if an object deep inside a heirarchical structure needs to communicate with another object somwhere else, how are you supposed to handle that with patchcords?

What am I not getting?

arne's icon

On 29/03/07, John Pitcairn wrote:
> Connecting everything by patchcords strikes me as imposing considerable constraints on the app structure.... if an object deep inside a heirarchical structure needs to communicate with another object somwhere else, how are you supposed to handle that with patchcords?

I agree, with a caveat.

I store data into value objects for use (as global variables) by other
patchers/functions. But when I need instantaneous notification, such
as a bang, I need to use send/receive pairs.

But the problem, in my mind, is not knowing what will happen at the
other end. A single bang sent "blindly" can inadvertently trigger a
multitude of actions if I'm not careful (further due to Max's
modularity via encapsulated patchers).

Yes, I can trace this by command-clicking on the send object, but still...

johnpitcairn's icon

Sorry, my post mutated somewhat during writing, and I meant to take the quote out of the above since it appears we're barking up roughly the same tree (no Max on this machine so can't look at your patch), but now I can't edit it. Is this the most annoying forum software around or what?

johnpitcairn's icon

Quote: arne wrote on Fri, 30 March 2007 12:03
----------------------------------------------------
> A single bang sent "blindly" can inadvertently trigger a
> multitude of actions if I'm not careful (further due to Max's
> modularity via encapsulated patchers).

If I need to send to multiple object inputs, I either use multiple unique sends (where the receivers are known and constant), or I use forward in conjunction with a coll containing the unique receive names, dump the coll, and send my message/bang after each coll output has set the destination. Either way, you have the destinations available to view in the source object.

So an OO way to handle your notifications might be to have every object that wants to be notified add their unique receive name to a coll in the notifier object. You'd send an "add receivename" message to the notifier object's unique receive name, rather than addressing the coll directly via a shared global coll. Then have the notifier process use the forwarding mechanism outlined above.

So you'd always have an explicit list of listeners, owned, set and managed by the notifier.

The use of the coll-forward mechanism does however incur some overhead compared to a straight single-send, multiple-receive mechanism.

johnpitcairn's icon

Quote: johnpitcairn wrote on Fri, 30 March 2007 13:35
----------------------------------------------------
The use of the coll-forward mechanism does however incur some overhead compared to a straight single-send, multiple-receive mechanism.
----------------------------------------------------

Though if that's troublesome, ie you're sending dense realtime data rather than config/setup messages, have your notifier object script the appropriate unique send into place and connect it, on receipt of the "add receivername" message. Make sure you also give that send a scripting name the same as its receivername (and store it in a coll), so a "remove" message can remove it.

Anthony Palomba's icon

My point is that send/recv is an asynchronous operation. Having to design
a patcher around this fact can make for some complexity that would
be unnecessary if you could use something like pattr. Pattr is an atomic
operation, you send a bang, you get the answer back. It is a mute
point because patter can not execute a subpatcher. Given that
fact, I guess one could use send/to simulate a function call.

Although come to think of it, is it really a good idea to have a global
object that I can call in to from any place? Or would it be more OO
like to instantiate an object and use inlets to execute functions?

Anthony

----- Original Message -----
From: "John Pitcairn"
To:
Sent: Thursday, March 29, 2007 5:42 PM
Subject: [maxmsp] Re: Re: Re: Re: How to apply oo within Max

>
> Quote: Anthony Palomba wrote on Thu, 29 March 2007 05:05
> ----------------------------------------------------
> > It would be
> > nice to not have to use send and recv pairs.
>
> I don't understand the "no send/receive" stuff. Sure, if you populate your
patcher/abstraction (object) with lots of send/receive entry/exit points,
that's asking for trouble, but what's wrong with scripting receives and
sends (or forward) into place at instantiation, with unique send/receive
names taken from an argument? Y'know:
>
> [r myobject.input1] and [s myobject.output1]
>
> How is sending a message to that myobject.input1 different from connecting
to a patcher input and sending a message?
>
> If listening for a message from the object's output send is problematic,
and you need the object to explicitly message another named receive (which
may change), then what's wrong with [forward], with the destination set on
the fly, either as an argument to the input message, or as a "set output1"
message (which could alternately delete/create the send if better
performance is crucial)?
>
> Connecting everything by patchcords strikes me as imposing considerable
constraints on the app structure. Patchcords are certainly desirable in
smaller structures to visualise the data flow, but if an object deep inside
a heirarchical structure needs to communicate with another object somwhere
else, how are you supposed to handle that with patchcords?
>
> What am I not getting?
>

johnpitcairn's icon

Quote: Anthony Palomba wrote on Fri, 30 March 2007 15:47
----------------------------------------------------
> Although come to think of it, is it really a good idea to have
> a global object that I can call in to from any place? Or would
> it be more OO like to instantiate an object and use inlets to
> execute functions?

Sure, and use an argument to choose one of the object's different public methods - like the zl object.

For the method to return something to the caller, if instantiated as a discrete instance, you'd just connect the object outlet to wherever you needed it in the calling object.

But this gets into some potentially heavy overhead if you instantiate, connect and de-instantiate an object by scripting on the fly just to call a public method.

Better to have the object created in the calling patch from the get-go, ie what we're all doing anyway - Max is already OO in this respect.

If, however, it needs to be more like a single static class that provides public utility methods (there are plenty of these in OO-land), and you never instantiate an instance of the class, we can choose among public methods with an explicit message and use route internally, but we don't have a mechanism to return anything to the caller, because a Max object has no idea where its input is coming from (2-way patchcords, anyone?).

So there would need to be an argument supplied with the input message that provides a return destination - I guess this would be a uniquely named receive or value in the calling object - and have the method either instantiate and set the value (um, overhead again?), or send via a forward.

The equivalent of calling a public method in a static class, passing in a reference to a variable that the method will stash a result in, with the caller owning the referenced variable...?

Joshua Kit Clayton's icon

On Mar 29, 2007, at 8:47 PM, Anthony Palomba wrote:

> My point is that send/recv is an asynchronous operation.

Not going to get deeply involved in this thread, but I'd like to
clarify that send/recv is a *synchronous* operation. It is simply
ambiguous as to ordering. Empirical tests should demonstrate this.

Otherwise, there's some interesting points here, many of which have
been tossed around at C74 in various forms. Basically, I think I can
say that there is a general move towards some of the OO ideas under
discussion without a lot of the OO dogma, the latter of which I
personally don't believe leads to any benefits except for those that
rely on conceptual aesthetics (who can blame those with such
fetishes, as it's arguably just a byproduct of our maladjusted
libido :) ). One can program OO in assembly language if one wishes.
Language constructs are simply conveniences.

However, I will say we are working very hard to make Max programming
a little more convenient. In the meantime, I would suggest figuring
out ways to work with Max as it is, or write external objects to
solve the problems which we have not solved to your satisfaction.

Happy Maxing,
Joshua

lawrence casserley's icon

Couldn't you make use of send/receive for this - send a list to a
named receive (the function input) - the list includes the input
parameters required by the function, and, most importantly, the
address of a receive in the calling function, which sets the send in
the called function to return its data _only_ to the calling function.

I think there are all sorts of virtues in send/receive - it just
needs clear thinking about how you use them - but good programming
needs that whatever system you use :-)>

Best

L

On 30 Mar 2007, at 05:41, John Pitcairn wrote:

> If, however, it needs to be more like a single static class that
> provides public utility methods (there are plenty of these in OO-
> land), and you never instantiate an instance of the class, we can
> choose among public methods with an explicit message and use route
> internally, but we don't have a mechanism to return anything to the
> caller, because a Max object has no idea where its input is coming
> from (2-way patchcords, anyone?).
>
> So there would need to be an argument supplied with the input
> message that provides a return destination - I guess this would be
> a uniquely named receive or value in the calling object - and have
> the method either instantiate and set the value (um, overhead
> again?), or send via a forward.

Lawrence Casserley - lawrence@lcasserley.co.uk
Lawrence Electronic Operations - www.lcasserley.co.uk
Colourscape Music Festivals - www.colourscape.org.uk

Isjtar's icon

interesting thread.
for me, an easier way of inheritance would be nice, though i wouldn't
know how to implement this.
i don't think "forcing" max to use oo concepts would lead to a more
oo-efficient way of working. the hassle of working around things
overturns the benefits of the oo approach imo.
but that's maybe because i'm not very proficient in oo and the
concept is still new to me.
that said, even the basics of it have some nice conceptual
advantages, especially as the structural visual approach of max loses
it's advantage when patches grow and the diagram feel is lost.

>
> However, I will say we are working very hard to make Max
> programming a little more convenient. In the meantime, I would
> suggest figuring out ways to work with Max as it is, or write
> external objects to solve the problems which we have not solved to
> your satisfaction.
>
> Happy Maxing,
> Joshua

is it me or are the max 5 teasers ramping up?

lists@lowfrequency.or's icon

also don't forget (or maybe people don't know) that in jitter you
have the "in2_name" property where you can refer explicitly to a
jit.matrix name without needing to use patch cords and the ever-
loving [t b l] object to trigger output.

i find jitter in general a bit more OO, with it's "@ttributes" that
make figuring out the name of your arguments to an object so much
easier (yes I'm mixing terms here, arguments are different than
attributes)

but unfortunately, given limited screen size, long names inside
objects can in the end make then fairly unreadable. it's tough to
hit that balance of readability, patching aesthetics, and usefulness,
that is part of any visual programming language with limited screen
space.

but as a side note, using OS X's built-in screen zoom function can be
very useful, especially during presentations.

cheers
evan

On Mar 30, 2007, at 11:00 AM, lawrence casserley wrote:

> Couldn't you make use of send/receive for this - send a list to a
> named receive (the function input) - the list includes the input
> parameters required by the function, and, most importantly, the
> address of a receive in the calling function, which sets the send
> in the called function to return its data _only_ to the calling
> function.
>
> I think there are all sorts of virtues in send/receive - it just
> needs clear thinking about how you use them - but good programming
> needs that whatever system you use :-)>
>
> Best
>
> L
>
> On 30 Mar 2007, at 05:41, John Pitcairn wrote:
>
>> If, however, it needs to be more like a single static class that
>> provides public utility methods (there are plenty of these in OO-
>> land), and you never instantiate an instance of the class, we can
>> choose among public methods with an explicit message and use route
>> internally, but we don't have a mechanism to return anything to
>> the caller, because a Max object has no idea where its input is
>> coming from (2-way patchcords, anyone?).
>>
>> So there would need to be an argument supplied with the input
>> message that provides a return destination - I guess this would be
>> a uniquely named receive or value in the calling object - and have
>> the method either instantiate and set the value (um, overhead
>> again?), or send via a forward.
>
> Lawrence Casserley - lawrence@lcasserley.co.uk
> Lawrence Electronic Operations - www.lcasserley.co.uk
> Colourscape Music Festivals - www.colourscape.org.uk
>
>

lawrence casserley's icon
Mattijs's icon

Hmm, I have a working example, but it has quite some deficiencies. I think the principle is interesting though, I decided to post it so that people can comment.

The biggest point of criticism I have is that passing id's is done by patch cords. I tried hiding and coloring some of them but even in an example as simple as this it clearly gets messy and hard to understand.

Cheers,
Mattijs

Mattijs's icon

Quote: lawrence casserley wrote on Fri, 30 March 2007 17:24
----------------------------------------------------

> Can anyone _really_ show that this way of thinking is intrinsically
> less satisfactory than an oo structure. I can see the benefits of oo
> at the conceptual level, but I think you can be just as strucutured
> with an inline approach. At the end of the day it needs disciplined
> and consistent thinking to produce a manageable program, whichever
> approach you use.
>

Lawrence, it is not about complex algorithms in relatively small amount of code. It is about relatively simple operations but an awful lot of different ones that all have to work together in a complex way.

Please bear in mind that effectively -every- software company (including Cycling 74) currently uses oo.

Best,
Mattijs

Jean-Francois Charles's icon
lawrence casserley's icon

On 30 Mar 2007, at 17:04, Mattijs Kneppers wrote:

> Lawrence, it is not about complex algorithms in relatively small
> amount of code. It is about relatively simple operations but an
> awful lot of different ones that all have to work together in a
> complex way.

Which is part of my point - the amount of code doesn't really matter
much, so reusing abstractions is no problem - a good abstraction will
usually be a fairly simple operation, and using it inline is clear
and easy to understand. I don't see that the structure I suggested
for mimicking calling a subroutine is _necessarily_ an improvement,
and it does make some complications - I think there are situations
where such a structure might be useful, but I would hate to impose it
in all circumstances. Max is a visual language and we should take
advantage of this, and when we obscure that visual flow, then it must
be for a really good reason.

My point about the assembler dsp was similar - repeating the code
didn't matter, but in that case (getting the absolute maximum out of
a what seem today to be a ridiculously slow processor was paramount)
the inline thinking was very important.

All of which may seem to imply that I always use only patchcords,
whereas in fact I find send/receives (with easy-to-understand names!)
frequently actually improve understandability. And anyway, I love to
play devil's advocate! ;-)>

Best

L

Lawrence Casserley - lawrence@lcasserley.co.uk
Lawrence Electronic Operations - www.lcasserley.co.uk
Colourscape Music Festivals - www.colourscape.org.uk

Jean-Francois Charles's icon

Quote: jkc wrote on Thu, 29 March 2007 23:42
----------------------------------------------------

> Otherwise, there's some interesting points here, many of which have
> been tossed around at C74 in various forms. Basically, I think I can
> say that there is a general move towards some of the OO ideas under
> discussion without a lot of the OO dogma.
> One can program OO in assembly language if one wishes.
> Language constructs are simply conveniences.
> However, I will say we are working very hard to make Max programming
> a little more convenient.

It's good to read that. Many people learn OO approach through LISP (or Scheme...) Looking forward to discovering Max 5...
Jean-Francois.

johnpitcairn's icon

Quote: lawrence casserley wrote on Fri, 30 March 2007 22:00
----------------------------------------------------
> Couldn't you make use of send/receive for this - send a list
> to a named receive (the function input) - the list includes
> the input parameters required by the function, and, most
> importantly, the address of a receive in the calling function,
> which sets the send in the called function to return its data
> _only_ to the calling function.

Um, yeah, isn't that what I said, except use a forward rather than a send to return (unless you want to be deleting/instantiating sends all the time for different callers)? I certainly thought it was ;-)

> I think there are all sorts of virtues in send/receive - it
> just needs clear thinking about how you use them - but good
> programming needs that whatever system you use :-)>

Yes indeed. Send/receive can get a beginner into a lot of trouble...

johnpitcairn's icon

Quote: Mattijs wrote on Sat, 31 March 2007 02:26
----------------------------------------------------
> To johnpitcairn: you're right that send/receives are very
> usable (and a good choice performancewise, see the 'pattr
> performance' thread), but only if you can make sure that there
> are no duplicate names involved. Imo, this is only possible by
> using generated unique id's in s/r names. You talk about
> having a coll with all unique send/receives, but I'd say OO
> involves having -a unique reference- (in whatever form) to all
> registered objects.

Sure, and since it's the "listener" object that tells the "notifier" to register it, the listener could supply the unique reference, using #0 to generate that at instantiation. I was omitting that for simplicity/brevity. So the coll inside the notifier would indeed contain unique references, generated by the lister and supplied to the notifier.

Example:

Max Patch
Copy patch and select New From Clipboard in Max.

save as "listener.mxt"
---------------------

Max Patch
Copy patch and select New From Clipboard in Max.

save as "notifier.mxt"
----------------------

Max Patch
Copy patch and select New From Clipboard in Max.

paste as new patch
------------------

johnpitcairn's icon

Quote: lawrence casserley wrote on Sat, 31 March 2007 03:24
----------------------------------------------------
> the function called in this way isn't
> really any different to an abstraction inserted into the patch
> inline, and will in most cases be less efficient.

For most usual purposes, it certainly will, though if the alternate "static" class/object/method only needs to return a result to one caller at a time (which will always be true as send/receive are synchronous?), the overhead involved in parsing the "caller" argument and setting a forward destination for the returned result is not huge.

But imagine a VERY complex patch that you want to use as an "inline function" in the normal Max way, instantiated a VERY large number of times. Imagine that this also needs to load some resource from disk and manipulate it so each instantiation's copy of the resource is slightly customized. That's going to take a LONG time to load all those instances, and possibly use a lot of memory.

I see this as a situation where a single static object providing public utility methods as we have outlined might be very advantageous - not least in avoiding the "why does it take 5 minutes to start up" complaints from users...

johnpitcairn's icon

Quote: johnpitcairn wrote on Sat, 31 March 2007 13:44
----------------------------------------------------
> and manipulate it so each instantiation's copy of the resource is
> slightly customized.

Uh, ignore that bit please, that would likely need to be done in separate instantiations anyway ... my point is that I've been down this route, where my load times were getting up around 2 minutes on a 1GHz G4 due to a zillion instantiations of an abstraction. Too long.

So I now load the abstraction just once, and send into it providing a unique return address for the result. I hadn't been thinking of it as a static class with public methods until this thread ;-)

vade's icon

Well, Im right with you, however I think its a real shame that we
have to work around those sorts of issues though - even relatively
simple abstractions loaded multiple times seem to be an issue, esp
with load times/close times.

Tis a shame.

On Mar 30, 2007, at 10:57 PM, John Pitcairn wrote:

> Uh, ignore that bit please, that would likely need to be done in
> separate instantiations anyway ... my point is that I've been down
> this route, where my load times were getting up around 2 minutes on
> a 1GHz G4 due to a zillion instantiations of an abstraction. Too long.

v a d e //

www.vade.info
abstrakt.vade.info

lawrence casserley's icon

On 31 Mar 2007, at 01:48, John Pitcairn wrote:

> Yes indeed. Send/receive can get a beginner into a lot of trouble...

But then so can a lot of other things in Max, or most any system for
that matter. Designing the system so that beginners are prevented
from making mistakes sounds pretty limiting to me.

Best

L

Lawrence Casserley - lawrence@lcasserley.co.uk
Lawrence Electronic Operations - www.lcasserley.co.uk
Colourscape Music Festivals - www.colourscape.org.uk

Trond Lossius's icon

Will this approach work if the abstraction contains timing functions
such as metro, line, delay or pipe, or is it limited to work only with
abstractions that provide nothing but instant feedback?

Also, are you able to use this approach for abstractions containing DSP
processing?

Best,
Trond

John Pitcairn wrote:
> Quote: johnpitcairn wrote on Sat, 31 March 2007 13:44
> ----------------------------------------------------
>> and manipulate it so each instantiation's copy of the resource is
>> slightly customized.
>
> Uh, ignore that bit please, that would likely need to be done in separate instantiations anyway ... my point is that I've been down this route, where my load times were getting up around 2 minutes on a 1GHz G4 due to a zillion instantiations of an abstraction. Too long.
>
> So I now load the abstraction just once, and send into it providing a unique return address for the result. I hadn't been thinking of it as a static class with public methods until this thread ;-)
>

Anthony Palomba's icon

I think the cases we have been discussing mainly focus around patchers
as repositories of data. Using OO methodology to access and organize
that data in such a way that makes it easy an efficient.
As far as DSP goes, I think OO would not help much other than being
able to have classes that encapsulate functionality, which is what you
are probably doing already.

Anthony

----- Original Message -----
From: "Trond Lossius"
To:
Sent: Sunday, April 01, 2007 8:32 AM
Subject: Re: [maxmsp] Re: Re: How to apply oo within Max

> Will this approach work if the abstraction contains timing functions
> such as metro, line, delay or pipe, or is it limited to work only with
> abstractions that provide nothing but instant feedback?
>
> Also, are you able to use this approach for abstractions containing DSP
> processing?
>
> Best,
> Trond
>
>
>
> John Pitcairn wrote:
> > Quote: johnpitcairn wrote on Sat, 31 March 2007 13:44
> > ----------------------------------------------------
> >> and manipulate it so each instantiation's copy of the resource is
> >> slightly customized.
> >
> > Uh, ignore that bit please, that would likely need to be done in
separate instantiations anyway ... my point is that I've been down this
route, where my load times were getting up around 2 minutes on a 1GHz G4 due
to a zillion instantiations of an abstraction. Too long.
> >
> > So I now load the abstraction just once, and send into it providing a
unique return address for the result. I hadn't been thinking of it as a
static class with public methods until this thread ;-)
> >

Mattijs's icon

Ok, I think I got something nice here, inspired by the recent posts in this thread.

This is a possible way to define an object with its methods and let them communicate according to OO principles.

Objects reference each other by keeping track of each others unique ID's. The 'call' abstraction retrieves the id of the target object and together with the 'object' abstraction, which generates a unique id, manages calling methods and delivering its return value.

Have a look at how 'call' gets info from its parent object in the updateUI method inside the filterGui object.

Please let me know what you think. Best,
Mattijs

--- save as 'object'

Max Patch
Copy patch and select New From Clipboard in Max.

--- save as 'call'

Max Patch
Copy patch and select New From Clipboard in Max.

--- main patch (note: has to be loaded to trigger loadbangs)

Max Patch
Copy patch and select New From Clipboard in Max.

Mattijs's icon

Hm the patch was posted on pc, I just opened the patch on a mac and the patches look crappy. Isn't this discrimination? Pc users are effectively banned from the communitiy this way. Who's going to take an ugly-looking patch serious? ;)

Anyways, Cmd+J will resolve most of the uglyness.

Mattijs

vade's icon

Hrm. This is definitely interesting Mattijs. Ill have to take a
really close look at this methodology and see how applicable it is in
real world usage. The patch is a little hard to follow even though it
uses so few objects. This is very very interesting stuff, and could
be very useful. Its a shame we have to spend so much time working
around Maxs' .... inefficient (for lack of a better word) handling of
many abstractions.

On Apr 1, 2007, at 4:12 PM, Mattijs Kneppers wrote:

v a d e //

www.vade.info
abstrakt.vade.info

Mattijs's icon

Quote: vade wrote on Sun, 01 April 2007 22:55
----------------------------------------------------
> Hrm. This is definitely interesting Mattijs. Ill have to take a
> really close look at this methodology and see how applicable it is in
> real world usage. The patch is a little hard to follow even though it
> uses so few objects. This is very very interesting stuff, and could
> be very useful.

Cool, let me know your findings. I'll start trying it in real life situations as well.

> Its a shame we have to spend so much time working
> around Maxs' .... inefficient (for lack of a better word) handling of
> many abstractions.

I totally know what you're saying. In fact I had to strip a big patch from all send/receive wrapper abstractions I made because the load time got unmanageable. 3 mins on a 2,5 GHz Quad. Debug that :p ..

At the moment I do everything with pattrs (handing in quite some flexibility compared with my abstractions), but I recently figured out that they cause quite some performance loss (as posted in another thread). Hopefully this new idea (which does actual communication with send/receives) can solve the problem.

Best,
Mattijs

>
> On Apr 1, 2007, at 4:12 PM, Mattijs Kneppers wrote:
>
>
> v a d e //
>
> www.vade.info
> abstrakt.vade.info
>
>
>
>
>
>
----------------------------------------------------

johnpitcairn's icon

Quote: Trond Lossius wrote on Mon, 02 April 2007 01:32
----------------------------------------------------
> Will this approach work if the abstraction contains timing
> functions such as metro, line, delay or pipe, or is it limited
> to work only with abstractions that provide nothing but
> instant feedback?

Metro might be problematic, unless all you want to do is send some regular result to multiple (registered) listeners, as per my example. Registering/deregistering listeners on the fly shouldn't be an issue.

For pipe, you'd need to maintain a FIFO list of the caller IDs in order to send the result back to the correct caller, but I don't see anything technically too troublesome in that, given one input = one output.

Line and delay can't have multiple inputs processing simultaneously anway (as you can for pipe), so that makes them inherently unsuitable for this approach unless you can guarantee only one caller at a time (which seems unlikely).

> Also, are you able to use this approach for abstractions
> containing DSP processing?

I don't use DSP at all, so I won't venture into any speculation here...

johnpitcairn's icon

Quote: vade wrote on Mon, 02 April 2007 08:55
----------------------------------------------------
> The patch is a little hard to follow even though it
> uses so few objects.
----------------------------------------------------

Agreed, I think I see the intent but a few more comments would certainly be helpful. When I have time I'll try building something using object and call to gain a better understanding of how it's implemented. It does look like we're jumping through a lot of hoops...

johnpitcairn's icon

Quote: johnpitcairn wrote on Mon, 02 April 2007 10:44
----------------------------------------------------
> For pipe, you'd need to maintain a FIFO list of the caller IDs
----------------------------------------------------

Second thought: no, you'd just use a separate pipe for the caller ID using the same parameters. Since pipe can remember the delay time as well as the value for each input, you'll be OK to call the process with a different delay time from a different ID, passing in value, ID, delay, ie:

Max Patch
Copy patch and select New From Clipboard in Max.

Stefan Tiedje's icon

Arne Eigenfeldt schrieb:
> I agree, with a caveat.

I agree even without caveat...

> I store data into value objects for use (as global variables) by other
> patchers/functions. But when I need instantaneous notification, such
> as a bang, I need to use send/receive pairs.

or use a pattr/named_object and a pattrforward in case you need a single
receive somewhere in you patcher hierarchy. If you need it global, there
is nothing wrong with global receives...

> But the problem, in my mind, is not knowing what will happen at the
> other end. A single bang sent "blindly" can inadvertently trigger a
> multitude of actions if I'm not careful.

But that "If you're not careful" will be true for any language
construction. Its more about how easy it is to be careful - Its much
easier since pattr...

If you use your own name space like within pattr, for example you place
a [pattrmarker MyGloriousPatch] into your main master patch and
consequently name all send/receives like [s MyGloriousPatch.MyGlobal].
You should be on the safe side. You can access local and global
variables at your will, and its pretty unlikely, that a 3rd-party
abstraction will use the same receive names...

Not being forced to be careful, is very helpful for small little
gadgets, as you have to type less. If your patch grows bigger, its just
your own responsibility to stay careful. It might require to rename send
and receives, an easy task with Textwrangler or alike...

To be able to rename elements recursively in a big patch would be great
though - (just dreaming, I am happy with Texwrangler... ;-)

Stefan

--
Stefan Tiedje------------x-------
--_____-----------|--------------
--(_|_ ----|-----|-----()-------
-- _|_)----|-----()--------------
----------()--------www.ccmix.com

Stefan Tiedje's icon

Anthony Palomba schrieb:
> Although come to think of it, is it really a good idea to have a global
> object that I can call in to from any place? Or would it be more OO
> like to instantiate an object and use inlets to execute functions?

A receive and a pattr are kind of inlets within a subpatcher...

And if your function is burried deep in the patcher hierarchy, cords are
a pain and make the whole thing unreadable/undebugable. If oo forces you
to bad patching I would refuse it...

Come to think of it, is it really a bad idea to have global
functions/variables?

Stefan

--
Stefan Tiedje------------x-------
--_____-----------|--------------
--(_|_ ----|-----|-----()-------
-- _|_)----|-----()--------------
----------()--------www.ccmix.com

Mattijs's icon

You're right. I'll make a new version with more comments probably tomorrow evening (+1 GMT). I'll also do it on a mac, that should make a big difference in readability :p

Mattijs

Quote: johnpitcairn wrote on Mon, 02 April 2007 01:22
----------------------------------------------------
> Quote: vade wrote on Mon, 02 April 2007 08:55
> ----------------------------------------------------
> > The patch is a little hard to follow even though it
> > uses so few objects.
> ----------------------------------------------------
>
> Agreed, I think I see the intent but a few more comments would certainly be helpful. When I have time I'll try building something using object and call to gain a better understanding of how it's implemented. It does look like we're jumping through a lot of hoops...
>
>
----------------------------------------------------

Mattijs's icon

Quote: Stefan Tiedje wrote on Mon, 02 April 2007 10:41
----------------------------------------------------

> To be able to rename elements recursively in a big patch would be great
> though - (just dreaming, I am happy with Texwrangler... ;-)
>

Absolutely. Refactoring; any eclipse users here? That is so powerful..

Mattijs

johnpitcairn's icon

Quote: Stefan Tiedje wrote on Mon, 02 April 2007 20:42
----------------------------------------------------
> Come to think of it, is it really a bad idea to have global
> functions/variables?
----------------------------------------------------

Yes/no/maybe...

Seems to me that in OO-design the idea of a static class vending public methods and constants allows "global" methods/constants without all the dangers of non-OO globals. You never instantiate an instance of the class, just call the class methods or retrieve the constants, and there aren't any class variables - or maybe that's just a C++/ObjC procedural concession? Heck, I'm a designer, Computer Science graduates feel free to flame me here...

I'm beginning to think that a very little (like < 20 lines) Java/JavaScript sprinkled over what we're doing might be extremely advantageous - if it weren't for the horrendous per-message overhead between Max and Java/JavaScript.

Durrr ... thinking ...

PS: Can we stop talking about "functions" and use "methods" instead, as an indication that we're trying to adhere to OO principles? Then at least we have discrete names for two different things ... (OK, I know, JavaScript, function, new, prototype, yaaah).

Uh ... is this making sense? I'm on deadline...

Mattijs's icon

Quote: johnpitcairn wrote on Mon, 02 April 2007 11:29
----------------------------------------------------
> Quote: Stefan Tiedje wrote on Mon, 02 April 2007 20:42
> ----------------------------------------------------
> > Come to think of it, is it really a bad idea to have global
> > functions/variables?
> ----------------------------------------------------
>
> Yes/no/maybe...

Yes. Except for statics.

>
> Seems to me that in OO-design the idea of a static class vending public methods and constants allows "global" methods/constants without all the dangers of non-OO globals. You never instantiate an instance of the class, just call the class methods or retrieve the constants, and there aren't any class variables - or maybe that's just a C++/ObjC procedural concession? Heck, I'm a designer, Computer Science graduates feel free to flame me here...

I think your example is exactly what a static class should be in Max. I definitly like your implementation.

Of course the core of oo lies in the non-static concepts. A static class in max is in fact a collection of global methods that can't depend on external variables and have a smart return system. We should definitly include the static class in the 'oo within max' examples. It's a typical example of a way of thinking that you don't run into automatically when working with max, but can have huge advantages, especially in load time and readability.

>
> I'm beginning to think that a very little (like < 20 lines) Java/JavaScript sprinkled over what we're doing might be extremely advantageous - if it weren't for the horrendous per-message overhead between Max and Java/JavaScript.

Yeah, as soon as we have the ideal conceptual way of working with oo in max, perhaps we can remake some stuff in javascript. Or maybe even earlier. I agree though about the max/javascript bridge. Would be another good reason to do some empirical testing.

>
> Durrr ... thinking ...
>
> PS: Can we stop talking about "functions" and use "methods" instead, as an indication that we're trying to adhere to OO principles? Then at least we have discrete names for two different things ... (OK, I know, JavaScript, function, new, prototype, yaaah).

Agreed. "method" from now on, I promise. :)

Mattijs

johnpitcairn's icon

So: I'm thinking about abstractions for "class" (non-static), with objects therein: "var" and "method", with appropriate OO behaviour, within reason. If this works out, the idea is that C++/ObjC/Java concepts are broadly usable (especially ObjC, with its explicit parameter:value arguments), with little conceptual translation.

Also possibly "constructor" and "destructor" as specific loadtime-only subclasses of "method".

Any classes (uh, objects, instantiations of file-abstractions) defined as "static" need to be guaranteed that a single instance per application/runtime will be instantiated. These vend out constants and global methods - a constant is simply a [v] that is set at runtime and cannot be changed. So any object defined with attribute "@static 1" needs to be checked for prior instantiations, and an error message posted if the object name exists. Hmm.

Exceptions to OO-standard: inheritance - has no sensible analogue that I can see in Max, ie you can't programaticaly (script/js/Java) specify extension and overriding of a class/abstraction without enormous trouble (?) - classes need to be file-based with manual modifications saved as a new file (class). But I don't see that as a bad thing, especially. Anyone?

Bottom line: this needs to be as easy to use as OO in C++, Java, ObjC, JavaScript, and ot incur huge overhad, or what's the point?

johnpitcairn's icon

What sort of shit is an edit-timeout of 1 minute??? Where did you guys get this forum software?? C'mon...

johnpitcairn's icon

Quote: Mattijs wrote on Mon, 02 April 2007 21:52
----------------------------------------------------
> I agree though about the max/javascript bridge. Would be another good reason to do some empirical testing.
----------------------------------------------------

I've done it. The translation between JavaScript - or Java, (presumably via JINI?) - and native C Max object in/out is prohibitively expensive for use with realtime-data, whether MIDI or (definitely) audio, unless the JavaScript/Java object does an enormous amount of processing for each event in/out.

JavaScript/Java objects interfaced with native Max are very nice for file i/o, config, preferences, UI or system calls, however...

johnpitcairn's icon

Quote: Mattijs wrote on Mon, 02 April 2007 21:52
----------------------------------------------------
> I think your example is exactly what a static class should be in Max. I definitly like your implementation.
---------------------------------------------------

Ta. LC Xmu makes a lot of use of this sort of thing, though I wasn't especially thinking OO as I developed it (don't look at the processing core, eek!).

To my mind, there's still a problem in that you can define a "static" class via patcher or instantiated abstraction - the patcher is akin to a class defined "inline" (as per inlined function), which while permissable in C++ etc strikes me as bloody dubious from an OO perspective.

I'm having an internal argument: should ALL patchers be saved to disk, a la .h/.cpp files? Is [p] illegal in the OO context we're talking about? Or is [p] a static class? In which case, should [p] include a mechanism to prevent more than 1 instantiation (easy enough given a unique name)? Or should [p] be overridden somehow in the search path to prevent its use? Hmm...

johnpitcairn's icon

Quote: johnpitcairn wrote on Mon, 02 April 2007 23:19
----------------------------------------------------
> In which case, should [p] include a mechanism to prevent more than 1 instantiation
----------------------------------------------------

Hmmm. Patcher [p] is not a class in any reasonable sense(?). [classname @static 1] should include a mechanism to prevent multiple instantiations (post error message), where classname = abstraction on-disk name. I'd be inclined to override [p] as an abstraction, requiring a unique @name attribute or it self-removes with load failure error?

johnpitcairn's icon

Quote: lists@lowfrequency.or wrote on Fri, 30 March 2007 23:53
----------------------------------------------------
> i find jitter in general a bit more OO, with it's "@ttributes"
> thatmake figuring out the name of your arguments to an object so
> much easier

The "@" argument syntax can be used in saved Max abstractions and [p] subpatchers, via the [patcherargs] object, unless I'm missing some limitation. Very reminiscent of the excellent Objective-C argument syntax, if I may say so...

Gary Lee Nelson's icon

Just tried this. In a subpatcher like

p thing 440 @one 1

"thing" is interpreted as the first of two regular arguments and not as the
intended name of the subpatcher. Not necessarily a problem, more of a
detail. Potentially quite useful. However, saving it as patcher yields
slightly different results on loading. Only 440 appears as a rehular
argument.

I point this out because, like some other maxers, I often develop an
abstraction as a subpatcher first. When it works, I save it into my own
library of abstractions.

Cheers
Gary Lee Nelson
Oberlin College
www.timara.oberlin.edu/GaryLeeNelson

On 4/2/07 8:18 AM, "John Pitcairn" wrote:

>
> Quote: lists@lowfrequency.or wrote on Fri, 30 March 2007 23:53
> ----------------------------------------------------
>> i find jitter in general a bit more OO, with it's "@ttributes"
>> thatmake figuring out the name of your arguments to an object so
>> much easier
>
> The "@" argument syntax can be used in saved Max abstractions and [p]
> subpatchers, via the [patcherargs] object, unless I'm missing some limitation.
> Very reminiscent of the excellent Objective-C argument syntax, if I may say
> so...

Stefan Tiedje's icon

jln schrieb:
> The main problem I see (at least in my example) is related to
> performance, as the global receive has to keep track of function
> calls orders. So I'm afraid the process can be really slowed down in
> some situations.

Most functions, are independent of other states of a patch, then I just
instantiate another subpatcher as my function with ins/outs locally and
I'm done. Another aproach is the use of a named coll, var or even a
buffer~. If I bang or dump it, it will output only locally. For example,
if I need a function which will output the last ten Midi notes which had
been played, I have a subpatcher, which fills 10 entries of a coll. That
coll would be accessible everywhere, and no "keep track of function call
orders is necessary.
Again, to think in terms of information flow seems to me a much clearer
way of thinking. And there are plenty of ways to pass and/access
information within Max...

Stefan

--
Stefan Tiedje------------x-------
--_____-----------|--------------
--(_|_ ----|-----|-----()-------
-- _|_)----|-----()--------------
----------()--------www.ccmix.com

Gary Lee Nelson's icon

I'll add jitter matrices to the list of data structures. They are very
useful for implementing multidimensional processes like markov chains.
Limited to numbers however.

On 4/2/07 9:45 AM, "Stefan Tiedje" wrote:

> jln schrieb:
>> The main problem I see (at least in my example) is related to
>> performance, as the global receive has to keep track of function
>> calls orders. So I'm afraid the process can be really slowed down in
>> some situations.
>
> Most functions, are independent of other states of a patch, then I just
> instantiate another subpatcher as my function with ins/outs locally and
> I'm done. Another aproach is the use of a named coll, var or even a
> buffer~. If I bang or dump it, it will output only locally. For example,
> if I need a function which will output the last ten Midi notes which had
> been played, I have a subpatcher, which fills 10 entries of a coll. That
> coll would be accessible everywhere, and no "keep track of function call
> orders is necessary.
> Again, to think in terms of information flow seems to me a much clearer
> way of thinking. And there are plenty of ways to pass and/access
> information within Max...
>
> Stefan

Cheers
Gary Lee Nelson
Oberlin College
www.timara.oberlin.edu/GaryLeeNelson

Gary Lee Nelson's icon

I am also using jit.cellblock a lot these days. Perhaps in a future version
of Max perhaps jit.cellblock can be named like coll, table, buffer~ and
jit.matrix.

On 4/2/07 10:51 AM, "Gary Lee Nelson" wrote:

> I'll add jitter matrices to the list of data structures. They are very
> useful for implementing multidimensional processes like markov chains.
> Limited to numbers however.
>
>
> On 4/2/07 9:45 AM, "Stefan Tiedje" wrote:
>
>> jln schrieb:
>>> The main problem I see (at least in my example) is related to
>>> performance, as the global receive has to keep track of function
>>> calls orders. So I'm afraid the process can be really slowed down in
>>> some situations.
>>
>> Most functions, are independent of other states of a patch, then I just
>> instantiate another subpatcher as my function with ins/outs locally and
>> I'm done. Another aproach is the use of a named coll, var or even a
>> buffer~. If I bang or dump it, it will output only locally. For example,
>> if I need a function which will output the last ten Midi notes which had
>> been played, I have a subpatcher, which fills 10 entries of a coll. That
>> coll would be accessible everywhere, and no "keep track of function call
>> orders is necessary.
>> Again, to think in terms of information flow seems to me a much clearer
>> way of thinking. And there are plenty of ways to pass and/access
>> information within Max...
>>
>> Stefan
>
>
> Cheers
> Gary Lee Nelson
> Oberlin College
> www.timara.oberlin.edu/GaryLeeNelson
>
>

Cheers
Gary Lee Nelson
Oberlin College
www.timara.oberlin.edu/GaryLeeNelson

Mattijs's icon
Mattijs's icon

As promised, the OO example, slightly modified, 'mac'ified and commented:

Let me know if something isn't clear and of course if you have ideas for improvements.

Best,
Mattijs

Stefan Tiedje's icon

Mattijs Kneppers schrieb:
> Hm the patch was posted on pc, I just opened the patch on a mac and
> the patches look crappy. Isn't this discrimination? Pc users are
> effectively banned from the communitiy this way. Who's going to take
> an ugly-looking patch serious? ;)

I always thought it is discrimination of the Mac users, they have to
repair all those ugly patches... ;-)

Just make all your object boxes a bit longer... always...

But a comment to your oo method:

On first sight it seems complicated. But there are circumstance where
this way of communicating does make sense. I imagine a UI for a filter
which controls several filters in a mixer. Then you need to
differentiate these. I did this with pattr and forward in the past.

But even if there is from time to time a need for complex communication,
it is not practical to use that for simple communication.

Any standardized methods have to solve the simple problems in a simple
way, and have to simplify the complex stuff. And they should be used
always then... (as patching style). The only project I can see which
would come close to this is still Jamoma. They have a standardized
communication structure. Else wise I would stick to pattr....
Or just subpatchers with inlet for calling the function and outlet for
return. If the function you call is very complex, lets say a database
and you need to avoid several instantiations of it, create a subpatcher
which communicates to the database and use several instantiations of
that calling subpatcher. And as I mentioned before, this could be a
simple coll or v...

Stefan

Max Patch
Copy patch and select New From Clipboard in Max.

--
Stefan Tiedje------------x-------
--_____-----------|--------------
--(_|_ ----|-----|-----()-------
-- _|_)----|-----()--------------
----------()--------www.ccmix.com

Stefan Tiedje's icon

vade schrieb:
> The patch is a little hard to follow even though it uses so few
> objects.

That's why I would suspicious of this as a useful technique. Anything
which is hard to follow (and you are an experienced Max user), is not a
considerable or good solution in my eyes.

> Its a shame we have to spend so much time working around Maxs' ....
> inefficient (for lack of a better word) handling of many
> abstractions.

It took me also quite a while to find out that it was my own lack and
inefficiency... ;-)
One of the big advantages of Max is its simplicity, if I need other
programming styles, I need to find my own way to do so. Its not so much
working around a lack of Max, its finding your own efficient patching style.

That's why this discussion is important. It needs a lot of different
views and perspectives to get a picture...

Stefan

--
Stefan Tiedje------------x-------
--_____-----------|--------------
--(_|_ ----|-----|-----()-------
-- _|_)----|-----()--------------
----------()--------www.ccmix.com

Stefan Tiedje's icon

Mattijs Kneppers schrieb:
> At the moment I do everything with pattrs (handing in quite some
> flexibility compared with my abstractions), but I recently figured
> out that they cause quite some performance loss (as posted in another
> thread).

I would not be concerned too much with the performance loss. The
advantages outperform the loss by far. The only efficiency which is
really important is the patching efficiency. Anything you can gain by
"hand optimization" is faster gained by waiting for the next faster
computer generation...

I remember, that I had that problem of very long load times as well, but
this was very long ago. Maybe my general patching style just solved it
without notice...

Stefan

--
Stefan Tiedje------------x-------
--_____-----------|--------------
--(_|_ ----|-----|-----()-------
-- _|_)----|-----()--------------
----------()--------www.ccmix.com

Stefan Tiedje's icon

John Pitcairn schrieb:
> What sort of shit is an edit-timeout of 1 minute??? Where did you
> guys get this forum software?? C'mon...

No timeout with thunderbird ever, I write even in the subway without
connection.... ;-)

Forums are for rare lurkers... ;-)

Stefan

--
Stefan Tiedje------------x-------
--_____-----------|--------------
--(_|_ ----|-----|-----()-------
-- _|_)----|-----()--------------
----------()--------www.ccmix.com

Stefan Tiedje's icon
Stefan Tiedje's icon

John Pitcairn schrieb:
> So any object defined with attribute "@static 1" needs to be checked
> for prior instantiations, and an error message posted if the object
> name exists. Hmm.

The only place I wanted to have this kind of notification that another
instance is loaded was in my copyright abstractions. I wanted the
copyright notice in the Max window to show up only once, and not with
every instantiation. That technique could be used for this kind of task.
But that would not prevent loading the instance, it would only prevent
printing the message...
And an instance which would kill itself on load was the most solid
method to crash Max... (Memory isn't too much of an issue nowadays)

But the idea of a "static" instantiation of a class is a very
"oo-centric" way of thinking, there is no need to bother I think. And
the static term was much more used as reference to global variables...

> Exceptions to OO-standard: inheritance - has no sensible analogue
> that I can see in Max, ie you can't programaticaly (script/js/Java)
> specify extension and overriding of a class/abstraction without
> enormous trouble (?) - classes need to be file-based with manual
> modifications saved as a new file (class). But I don't see that as a
> bad thing, especially. Anyone?

I have no problem with that. What I usually do, if I need to enhance an
existing abstraction, add some code to interpret new messages. Basically
add a parameter to the route object which is very often placed as first
object in my patches to route messages...

It will almost never break old patches...

Stefan

--
Stefan Tiedje------------x-------
--_____-----------|--------------
--(_|_ ----|-----|-----()-------
-- _|_)----|-----()--------------
----------()--------www.ccmix.com

johnpitcairn's icon

My current thinking:

CLASS

A - an abstraction saved on disk

B - a patcher - in which case it's only ever a single instance, even if simply duplicated (because then you have two discrete class definitions, not two instantiations).

It would be nice if we could treat patcher/abstraction as (almost) fully interchangeable while developing, but there are obvious problems with the #0 syntax not working in a patcher, and with pvar and pv not being local to a single patcher.

I'd like to define an instantiation of a "class", as either "public" (input accessible from anywhere), or "private" (input only accessible from its parent patcher).

Private is pretty obviously just a regular abstraction/patcher, with regular inlets/outlets. No problem there.

Public would ideally use a receive object bound to a unique name that reflects the patcher heirarchy, ie "L1.L2.L3".

I'm working on a javascript object to handle this, based on a "@pub name" attribute. This javascript object (present in any class) figures out the heirarchy, auto-names the containing class (preventing duplicate names in the same scope) and sets the receive source. If the @pub attribute isn't there, the receive is replaced with an inlet.

Yes, this is very similar to pattrforward, isn't it? But Stefan's comments notwithstanding, I really do want to keep my CPU use to a minimum if I'm a MIDI background app running on an older CPU, and the interesting activity is occurring in some other CPU-intensive app. I need to do some pattrforward tests I guess...

Optional: an @static attribute that would allow a single working instantiation. Subsequent instantiations would load a simpler version that merely communicate with the singleton.

METHOD

You could consider any processing internal to an abstraction or patcher as a method.

Public method - the parent class is responsible for despatching calls to any internal processes that could be considered "public", ie [route method1 method2 ... ]

Private method - processing that can't be directly accessed from the class inlet (or receive).

[pvar] use is not appropriate, as its behaviour is different in abstraction vs patcher?

VARIABLE

Still thinking about the idea of class variables.

Obvious issues with [pv] in a patcher as opposed to an abstraction.

In general, public class variables should be avoided, with public get/set methods preferred where necessary.

A private static class variable doesn't look possible?

INHERITANCE

Sure, you can copy an abstraction/patcher and add stuff, but that's not true inheritance. What happens if the superclass definition changes? The subclass doesn't know it has a superclass, and won't inherit the changes.

johnpitcairn's icon

Quote: johnpitcairn wrote on Wed, 11 April 2007 11:09
----------------------------------------------------
> Yes, this is very similar to pattrforward, isn't it? But
> Stefan's comments notwithstanding, I really do want to keep my
> CPU use to a minimum if I'm a MIDI background app running on
> an older CPU, and the interesting activity is occurring in
> some other CPU-intensive app. I need to do some pattrforward
> tests I guess...

And so I have. Sending to the inlet - ie including a [prepend in0] - of a patcher via pattrforward is about five times slower than sending to a receive object via send. Not an option for realtime data for me, unfortunately.

Mattijs's icon

Hi johnpitcairn,

I'm interested to see how your implementation will work. I have several comments, but maybe I should wait until you made a prototype. If you could use help, for example with javascript issues, let me know. I work with javascript a lot lately.

The idea of using the position in the hierarchy to compose the unique id that we've been discussing, looks promising.

I'm curious as to your thoughts about my latest example. More so if you simply didn't feel like checking it out because it looked messy; any comments are appreciated.

Cheers,
Mattijs

uote: johnpitcairn wrote on Wed, 11 April 2007 01:09
----------------------------------------------------
> My current thinking:
>
> CLASS
>
> A - an abstraction saved on disk
>
> B - a patcher - in which case it's only ever a single instance, even if simply duplicated (because then you have two discrete class definitions, not two instantiations).
>
> It would be nice if we could treat patcher/abstraction as (almost) fully interchangeable while developing, but there are obvious problems with the #0 syntax not working in a patcher, and with pvar and pv not being local to a single patcher.
>
> I'd like to define an instantiation of a "class", as either "public" (input accessible from anywhere), or "private" (input only accessible from its parent patcher).
>
> Private is pretty obviously just a regular abstraction/patcher, with regular inlets/outlets. No problem there.
>
> Public would ideally use a receive object bound to a unique name that reflects the patcher heirarchy, ie "L1.L2.L3".
>
> I'm working on a javascript object to handle this, based on a "@pub name" attribute. This javascript object (present in any class) figures out the heirarchy, auto-names the containing class (preventing duplicate names in the same scope) and sets the receive source. If the @pub attribute isn't there, the receive is replaced with an inlet.
>
> Yes, this is very similar to pattrforward, isn't it? But Stefan's comments notwithstanding, I really do want to keep my CPU use to a minimum if I'm a MIDI background app running on an older CPU, and the interesting activity is occurring in some other CPU-intensive app. I need to do some pattrforward tests I guess...
>
> Optional: an @static attribute that would allow a single working instantiation. Subsequent instantiations would load a simpler version that merely communicate with the singleton.
>
>
> METHOD
>
> You could consider any processing internal to an abstraction or patcher as a method.
>
> Public method - the parent class is responsible for despatching calls to any internal processes that could be considered "public", ie [route method1 method2 ... ]
>
> Private method - processing that can't be directly accessed from the class inlet (or receive).
>
> [pvar] use is not appropriate, as its behaviour is different in abstraction vs patcher?
>
>
> VARIABLE
>
> Still thinking about the idea of class variables.
>
> Obvious issues with [pv] in a patcher as opposed to an abstraction.
>
> In general, public class variables should be avoided, with public get/set methods preferred where necessary.
>
> A private static class variable doesn't look possible?
>
>
> INHERITANCE
>
> Sure, you can copy an abstraction/patcher and add stuff, but that's not true inheritance. What happens if the superclass definition changes? The subclass doesn't know it has a superclass, and won't inherit the changes.
>
>
----------------------------------------------------

johnpitcairn's icon

Quote: Mattijs wrote on Wed, 11 April 2007 20:23
----------------------------------------------------
> I'm curious as to your thoughts about my latest example. More
> so if you simply didn't feel like checking it out because it
> looked messy; any comments are appreciated.

Checked it out, and it makes sense but it seems too opaque and thus unintuitive to use (to me). Rather than asking an object for its unique id in order to talk to it, I'd prefer to use a more common oo-style scope heirarchy to talk to things:

[p @name L1 [p @name L2 [p @name L3]]]

whereby there are receive objects inside each of those (public) patchers:

[r L1], [r L1.L2] and [r L1.L2.L3]

So you have the local names visible as a patcher argument at each level of the heirarchy, making it easy to construct an appropriate reference.

The javascript I'm working with at the moment does also provide a unique ID (integer) for each instantiation, which could be used if desired.

I've not posted anything yet due to lack of time to work on it, and I did want to find out if pattr could be made to do some of the work.

johnpitcairn's icon

Quote: johnpitcairn wrote on Wed, 11 April 2007 15:05
----------------------------------------------------
> And so I have. Sending to the inlet - ie including a [prepend
> in0] - of a patcher via pattrforward is about five times slower
> than sending to a receive object via send.

BUT! Sending to a named [route] within the patcher via pattrforward incurs far less penalty than sending to the inlet - only about 20% slower than using a [receive] connected to that [route], and about 10% faster than using [forward].

That I can live with, considering I'll likely be needing that route and/or forward anyway...

Mattijs's icon

Quote: johnpitcairn wrote on Thu, 12 April 2007 03:07
----------------------------------------------------
> Quote: Mattijs wrote on Wed, 11 April 2007 20:23
> ----------------------------------------------------
> > I'm curious as to your thoughts about my latest example. More
> > so if you simply didn't feel like checking it out because it
> > looked messy; any comments are appreciated.
>
> Checked it out, and it makes sense but it seems too opaque and thus unintuitive to use (to me).

Ah, thanks for the feedback.

> Rather than asking an object for its unique id in order to talk to it, I'd prefer to use a more common oo-style scope heirarchy to talk to things:
>
> [p @name L1 [p @name L2 [p @name L3]]]
>
> whereby there are receive objects inside each of those (public) patchers:
>
> [r L1], [r L1.L2] and [r L1.L2.L3]
>
> So you have the local names visible as a patcher argument at each level of the heirarchy, making it easy to construct an appropriate reference.

I'd be happy to test and comment your prototypes. I have been working on a similar approach once and I stumbled upon some drawbacks. But I might not understand your idea completely so I'll wait with that until you made a patch.

In the mean time I'm trying my ideas in practice, to see if I can make a demo application that is built on these oo principles and clearly displays its advantages. If everything works I'll start using the principles in our (huge) main project patchers.

>
> The javascript I'm working with at the moment does also provide a unique ID (integer) for each instantiation, which could be used if desired.
>
> I've not posted anything yet due to lack of time to work on it, and I did want to find out if pattr could be made to do some of the work.

I understand completely. Btw, in my experience, the closer you get to the edge of max, the more timeconsuming this kind of ideas get ;)

Mattijs

johnpitcairn's icon

A few further thoughts:

1 - Do we REALLY need to consider [p] as a class instantiation?

If we limit "class" to saved-to-disk abstractions only, then life becomes a lot simpler - # arguments work, pv and pvar have local scope and can be used as private variable and private method call, respectively.

If p is not a class instantiation, then it's simply a convenient way of hiding complexity within the same local scope. No equivalent in oo-speak (or is there?), but then, this is Max.

2 - Is pattrforward to named objects in a class really a workable solution for "public" access?

It's simple and elegant, sure, but objects may be named for other reasons (scripting access, pvar access), and this exposes them as public.

Can a signal be sent via pattrforward?

dlurk's icon

John Pitcairn wrote:
> If p is not a class instantiation, then it's simply a convenient way
> of hiding complexity within the same local scope. No equivalent in
> oo-speak (or is there?), but then, this is Max.

There is an equivalent and it's broader than OO: [p] == {}

In Max, as an IDE of sorts, we use [p] as collapsible braces.

johnpitcairn's icon

Ah yes - though the "collapsibility" is editor-specific.

But another problem arises if p = {} and pattrforward is used for method despatch - pattrforward needs to see the [p] as part of the object heirarchy to access anything therein. So for a structure like:

[abstraction @public L1 [p [abstraction @public L2]]]

I'd want to address stuff in L2 as simply [pattrforward L1::L2], with the p not defining its own scope (consistent with the behaviour or pv and pvar).

But pattrforward addressing would need [pattrforward L1::(pname)::L2], since as far as pattrforward is concerned, p does define its own scope.

Rats.

johnpitcairn's icon

Here's what I arrived at so far: a little js to manage scoping/naming/ids, and pattrforward with unique receives for public method calls with return values.

Class objects (patchers or abstractions) get auto-named according to their @public (name) attribute.

Any named object is exposed to pattrforward as public, so scripting access is a potential problem.

This doesn't deal with private scoping at all - there are differences using pv and pvar in patchers vs abstractions that make this a tricky prospect, and pvar requires a name, thus it's not private.

All javascript executes at object instantiation, so there's no js processing overhead otherwise.

Comments invited...

Jeremy's icon

no

jb

Am 12.04.2007 um 23:16 schrieb John Pitcairn:

> Can a signal be sent via pattrforward?

Mattijs's icon

John, that's nice! It looks like we're really building towards something! I'm going to have a close look at it this weekend, but for now I already have some quick comments (please forgive me for only mentioning the criticism, not the praise. I really like your implementation in multiple ways).

- generating a unique id could be done more easily with an abstraction getID:

Max Patch
Copy patch and select New From Clipboard in Max.

- the possibility to have subpatchers as well as abstractions for classes is vital in practice, I am afraid. But I can live with copy-pasting the p.
- why name patchers/classes with @public attribute instead of just name them manually? One important pro would be that max automatically generates a unique name by appending array style [x] if you make a copy of a named object, where x is an increasing number. Very useful, I think, to define arrays of classes, which is definitly what I'm going to do. For now I don't see an urgent need to distinguish between private and public classes (which I think you are after).
- as you mentioned in the other thread, this system doesn't allow for unnamed intermediate subpatchers
- public float divide(int, int, symbol): You could leave the symbol out of this description as far as I am concerned, because it is part of the function calling protocol, not of the actual method.
- maybe using a simple abstraction (empty, perhaps, except for a script that sets its object name?) instead of the route for a function call could make a difference in intuitivity
- you didn't deal with variables yet, only with methods.
- v, pv and pvar: I don't understand your comment completely. My version: v is not useful because it requires a global name. pvar is not useful because it has only a local scope and there is no way to access it from outside. pv on the other hand, I don't understand your comment about its scope. As far as I can see it behaves similar in abstractions and subpatchers. There -is- an issue that it resets its peers' value when instantiated inside an abstraction, but that simply looks like a bug to me, not a conceptual problem. pv could be very useful.
- There is another, more advanced, issue with pv. It can't be overridden. I am working on a prototype that explains this. I hope to finish it this weekend.

But I'll give everything a closer look this weekend, perhaps I'm able to make an updated version of your implementation which incorporates some of my comments.

Cheers,
Mattijs

Quote: johnpitcairn wrote on Fri, 13 April 2007 07:33
----------------------------------------------------
> Here's what I arrived at so far: a little js to manage scoping/naming/ids, and pattrforward with unique receives for public method calls with return values.
>
> Class objects (patchers or abstractions) get auto-named according to their @public (name) attribute.
>
> Any named object is exposed to pattrforward as public, so scripting access is a potential problem.
>
> This doesn't deal with private scoping at all - there are differences using pv and pvar in patchers vs abstractions that make this a tricky prospect, and pvar requires a name, thus it's not private.
>
> All javascript executes at object instantiation, so there's no js processing overhead otherwise.
>
> Comments invited...
----------------------------------------------------

Mattijs's icon

Quote: Mattijs wrote on Fri, 13 April 2007 11:23
----------------------------------------------------
> - as you mentioned in the other thread, this system doesn't allow for unnamed intermediate subpatchers

Sorry, I mean in *this* thread.

Stefan Tiedje's icon

John Pitcairn schrieb:
> INHERITANCE
>
> Sure, you can copy an abstraction/patcher and add stuff, but that's
> not true inheritance. What happens if the superclass definition
> changes? The subclass doesn't know it has a superclass, and won't
> inherit the changes.

Inheritance would then be an abstraction around another abstraction,
that would fit your definition.

Max Patch
Copy patch and select New From Clipboard in Max.

--
Stefan Tiedje------------x-------
--_____-----------|--------------
--(_|_ ----|-----|-----()-------
-- _|_)----|-----()--------------
----------()--------www.ccmix.com

johnpitcairn's icon

Quote: Stefan Tiedje wrote on Sat, 14 April 2007 18:27
----------------------------------------------------
> Inheritance would then be an abstraction around another
> abstraction, that would fit your definition.

OK for method inheritance/overriding, but won't this run into problems if you want to do something in the derived class using variables defined in the original class? ie:

class original()
{
private p;
}

class new extends original()
{
method getp()
{
return p;
}
}

No way to do this in max-land if the original abstraction provides no way to retrieve the variable?