The virtues of using [value] in large projects

Leigh Marble's icon

For many years, I have been sending messages around large Max patches using send and receive objects. However, I'm starting to see the benefits of using value objects.

The biggest drawback to value objects, and why I avoided them previously, is that if you update a value, it doesn't spit that new value out of all other named instances of value. So in that sense, it's less "immediate" than just sending messages using send/receive.

However, as patchers start to grow in complexity, keeping track of all the variables becomes more difficult. One solution is to place a copy of all value objects in a single subpatcher, and have that patcher act as both a sort of "header" file for the whole patch (to answer the frequent question "now, what the hell did I name that variable?"), as well as allowing you to see the state of every variable across the entire patch at once. It's also a good central place to write in your comments about each variable.

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

Here's an example "global_values" subpatcher. Note the use of the [active] object to bang all the value objects in the patch any time the subpatcher window is opened or brought to the front.

As an extension of this idea, I thought of a possible design pattern for creating large patches: all values go into value objects, and send/receives are used for bangs only. The one exception would be for sending messages to display objects – if the destination of a particular message is simply to be shown on screen for the user to read, then you might as well just throw it down a send/receive chute, rather than go through the effort to save it into a value.

Obviously, using variables in programming isn't a ground-breaking idea! But using variables within Max has been counter to my habits for so long, that this seems like an idea worth sharing and exploring.

cheers,
Leigh

Leigh Marble's icon

In working with this concept, I started to create some abstractions to make working with value objects easier.

Here's one abstraction, called gate_by_value. You instantiate the abstraction with a named value as the 1st argument, for example: [gate_by_value pass_program_changes].

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

Then, the object will gate incoming messages, depending on the value stored in a [value pass_program_changes] object:

gate_by_value also takes a 2nd argument, to invert the use of the value. For example say that you had already named the value object as "block_program_changes", instead of "pass_program_changes". Any non-zero integer as the 2nd argument inverts the use of the value, so that you could say this:

[gate_by_value block_program_changes 1]

instead of this:

[gate_by_value pass_program_changes]

Floating Point's icon

thanks for the post, very "value-able";
I use v for saving button/toggle etc states, whether a file was properly loaded/exists etc
very handy abstraction

Chris Vik's icon

What about using something like [pvar]?

This gives you the benefit of a [send]/[receive] "push" while being able to centralise the variables in one location.

Leigh Marble's icon

I think [pvar] can be useful to organize a patcher that is presenting a GUI layer.

However, the big drawback to [pvar] is that the [pvar] objects have to exist in the same patcher level in order to share their values. In other words, they don't do "global" variables. (I would guess that the name "pvar" comes from "private variable", although I don't know that for sure.)

Chris Vik's icon

Ah yes - you're right. It even says so in the example.

Leigh Marble's icon

Since [value] is the only global variable object (that I know of), I was starting with it as the basis for this new way of organizing large patchers. I found a related discussion, about the pros and cons of value-sharing objects, on the forums here.

Anyways, as I mentioned above, one way to start thinking about this use of [value] objects is:

all values go into value objects, and send/receives are used for bangs only

To elaborate on that: you SET the value when you know the value. You GET the value when you use the value. The "knowing" and the "using" are split into two steps, rather than having the knowing be the trigger for action by itself.

As a side-effect of this split, I have been using a bunch of send/receive pairs named "update_{VARNAME}", to send a bang whenever some part of the patch needs to know when a value has been updated.

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

Or, instead you could use a value "observer", if a value is frequently updated and you want to avoid the extra overhead of a send/receive pair and other supporting objects (like a [t b i] object, likely). Here's a basic example of an observer, that I have saved as an abstraction named [value_observer]:

Floating Point's icon

if it's a continual polling i'd put a deferlow between the metro and the value objects

Chris Vik's icon

Also, have you looked into using [dict]? I've found that to be a great way to have globally available variables with the added benefit of having a great hierarchical structure. Easy to serialise, store and recall.

Leigh Marble's icon

Great ideas guys, [deferlow] and [dict] are two objects that I remain fuzzy about, but I will read up more on them today...

Leigh Marble's icon

OK...

[deferlow] is pretty straightforward, and although I don't understand the full implications of the high vs low priority threads, I can see why in general you'd want to deferlow a polling [metro].

[dict] I have never used before. So, it's a multidimensional, associative array, with global access to its contents?

When I claimed above that [value] is the only global variable object, I was leaving out [coll] (since I was thinking of single-value objects). [coll] is already usable as an associative array, so I suppose the chief advantage of [dict] is that it allows for nested/multidimensional structure?

In the past, I have "rolled my own" multidim structure with [coll]. I can see how [dict] could make that a slicker proposition.

Besides that, what are the other advantages to [dict]? (Besides being able to easily export data to a JSON file.)

Leigh Marble's icon
Chris Vik's icon

There's quite a few benefits. ^^ That's a really good thread to read about it.

I think the bottom line is, it's fast, global, can use whatever hierarchy structure you like, and works best when used from JavaScript.

Leigh Marble's icon

I noticed that part about being able to access the data in a [dict] from JavaScript. That's huge!

AudioMatt's icon

The value object one of the key objects that makes my general rule of thumb possible when patching"

Non-audio cables should never cross except above the gate object

Leigh Marble's icon

So, I've been working with dictionaries a bit more, mostly accessing them through Javascript, and for big projects it's really going to be a game-changer.

Aside from offering a hierarchical data structure, and being able to read and write from dictionaries using Javascript, there's another big plus: built-in import/export of data from/to JSON files. I have been working on some scripts that should allow dictionaries to replace autopattr/pattrstorage as a method of saving settings to disk. I like the pattr system just fine, but so far it seems like its data is "walled off" from Javascript access... I haven't done much hacking on that myself, but this thread states: "there is no way to manipulate [a UI object's] state through pattrstorage in JS direct way", and later "JS object pattr’ized can hold only its own parameters , nor can access content of a pattrstorage in a direct way".

As others have noted elsewhere, documentation on Max Dictionaries is a bit thin. The essential Dictionary methods accessible from Javascript are listed here:
https://docs.cycling74.com/max6/dynamic/c74_docs.html#dictionaries

In addition to those methods, I'm making use of these basic Javascript techniques for accessing Max objects, so I can grab and set values from UI objects:

// set a variable for a Max object, by referencing its scripting name
var maxobject = this.patcher.getnamed('the_object's_scripting_name');
//
// get the object's current value
var currentvalue = maxobject.getvalueof();
//
// set the object's value
maxobject.set(29);

And, lastly, I'm playing around with a naming standard for the UI objects' "scripting names", so that they will be named in a consistent way according to the hierarchical data structure. For example, if you look at the "hierarchy" tab of dict.maxhelp, you will see that nested dictionaries can be accessed using double-colon syntax (::), e.g. wheels::front::spokecount. If I want a UI object to be updated with the value held by that nested key, I would name the UI object wheels--front--spokecount (you have to substitute double dashes for the double colon because Max does not allow colons in scripting names).

The benefit of establishing a naming convention like that is that you don't need to store into some additional table (or always specify in custom Javascript functions) which Dictionary values are supposed to be displayed where. You just write a Javascript function traverse your whole Dictionary and blast out values with maxobject.set() messages. And then any values you care about showing to the user, you create and name appropriate UI objects for.

Leigh Marble's icon

PS: There are updated Max 7 docs for the Dict object in JS, although I haven't checked to see if all of these are accessible in Max 6:
https://docs.cycling74.com/max7/vignettes/jsdict