Max Message Semantics

taylor's icon

I have a computer science background and an interest in programming languages. So I'm curious about why max passes messages the way it does. Take the following extremely simple example:

Changing the left number box causes + to be executed. Changing the right number box does not. This is because as a convention, Max uses the leftmost inlet to trigger computation (the "hot" inlet).

According to tutorial 6: "It is a common practice, when a cold inlet needs to produce output, to apply a bang message (via a button or trigger object) into the left-most inlet to force output."

What is the reason for this left-hot convention as opposed to any inlet triggering computation?

thanks!

ecuk's icon

A quick but rather incomplete answer is that this allows you to synchronise the effect of changes to multiple parameters. A simple example is when you want to have the output message synchronised to the bang from a metro object, say every 250ms. You can send messages to all of the cold inlets to set the other parameters first, then only trigger the object’s ‘computation’ and resulting output at the desired time. If all inlets were hot, doing this would be more (or at least differently) convoluted.

If, as you say, you come from a computer science background, you might want to look at the SDK documentation. The left-hot convention is just that, a convention (and arguably a good one). If you create your own objects, you can freely do whatever you want.

Roman Thilenius's icon

i believe it simply was the idea that in most cases the average, future user of the max enviroment will more often not want an external to execute than to do so.

in case you dont know yet, when max was born, it mainly was dealing with (pseudo) midi data.

in fact, it is sometimes even for the first inlet a PITA that you have to do the [prepend set] hand gesture so often.

but it really depends on the application. there are also situations where you want to trigger a list or a string assembled from parts before from all inlets or maybe only from the fifth.

for example, in the case of assembling an array, they added [pak] to max 15 years after [pack] - because users requested so.

or maybe those exceptions only confirm the rule? :)

Roman Thilenius's icon

p.s.

note that you can also send the list "53 40" to the first inlet.

and there are many other scenarios where both values will be triggered from the same place. and of course you only want to output the result one time.

Roman Thilenius's icon

would you expect from expr 53 + 40 (in a higher level language) to calculate the result twice? :)

ecuk's icon

Agreed, Roman. One of the reasons that I said that my original answer was ‘incomplete’ is that I was avoiding the history behind all of this. (The other reason is that I was avoiding the question of why the left inlet is conventionally hot and the others cold. Again, early Max history.)

You could think of the hot-inlet thing as being similar to the eager evaluation semantics of traditional C-like languages. In these you first calculate the values of all of the parameters to a function call and only then do you ‘compute’ the function itself. A similar thing happens in Max, where might think of it as first gathering up all of the parameter values coming in via the cold inlets and only ‘call’ the object when a message arrives at a hot inlet. Or something like that. This is more or less how I like to make sense of it, anyway.

Roman Thilenius's icon

well, i did not see your answer when i posted (seems the posting order issue is back since the forum went away from wordpress) but nice to hear that i might have added the missing parts. :)

i find it interesting that the OP asked for the "semantics" of things but, as it seems, did not take the third option into account where the hot inlets would be on the right.

and as you can see, i wasnt able to answer the semantic stuff, rather i found a workaround (something typically for max?) and claimed it all to be engineering decisions.

of course nobody really knows. maybe they just invented the hot dog inlets to provoke that we can discuss the reasons 30 years later.

ecuk's icon

I felt inspired by your comment to find what was in fact said all those many years ago, so I just dug out my copies of some of Miller Puckette’s early papers on Max…

In his 1988 paper on The Patcher, he says, ‘An object is usually taken to be its own first inlet, through which you may direct any desired message to the associated object.’ I take this to mean the he conceived of the leftmost inlet as being the primary way to send messages to an object, indeed this inlet is essentially the object itself. Kinda makes sense in a 1980s, object-oriented way; think Smalltalk or the lesser-known Self (which was the key inspiration for JavaScript’s prototype-based object model).

A few years later, in his Computer Music Journal article on Max in 1991, he says, ‘By convention, if an object has more than one inlet, its leftmost inlet is the 􏰓“active”􏰔 one; passing a message to that inlet causes something to happen,􏰅 and passing messages to the other inlets simply changes the state of the object.’ I read this to mean that he envisioned more or less the semantics that I suggested in my earlier reply, although he says it rather more clearly and concisely.

Other than this, it is worth noting that Miller Puckette has always been rather clear that, despite its appearance, Max (and Pd, for that matter) is an object-oriented language and not a dataflow language. This is in part because objects ‘usually contain some local state’ (CMJ 1991 again). Cold inlets make it possible to set this state without always having to do the [prepend set] thing in order to create state-setting messages to send to the object’s leftmost inlet.

Max might not be perfect—like all programming languages, it certainly has its idiosyncrasies—but it does more or less what it was intended to do, and it does it reasonably well.

taylor's icon

Timing and OOP make sense as justifications for the semantics.

An alternate semantic would be to have all-hot inlets for most objects, and have a special object that sends a message when it receives a bang (a single cold inlet... is there already such a thing?). Would that make for a usable system? What would the consequences be? (I will probably be able to answer this myself as I become more experienced with Max, but please share if you have some insight :-)

Roman Thilenius's icon

another aspect is that there must be different types of inlets, with one of them having a form of priority to the other, because that is a natural law for many other things, too.

for example you also have different threads with different priorities in programs, and the idea behind that is probably also not exclusively a practical reason.

Roman Thilenius's icon

regarding your question: there are surely some cases where you might want to build an abstraction patcher yourself which has different inlets for hot and cold versions of the same input.

and i probably would not need to explain what my [110.++] abstraction does.

Robin Parmar's icon

This is a good discussion about something I have needed to consider as a teacher.

The simplest way to structure input to objects is to have only one inlet, to which all commands go. The first word in the command indicates the action to take and the rest are parameters. A special word indicates that output should be produced. In Max this special word became a "bang".

However, for convenience, further inlets were added to objects, so that common parameters could be changed directly. This is fine in theory, so long as these inlets are entirely optional. In other words, all operations should be possible by sending the appropriate commands to the left inlet. This is a requirement for logic and consistency.

Unfortunately, a good number of inconsistencies crept into the library. Some of these are for convenience, to make common cases easier to handle and less verbose. For example, you don't need to bang a math object, you just send it the float or integer. Hence the concept of a "hot" inlet. This is one that produces output without a bang.

ecuk's icon

Fully agreed, Robin, it would, in theory, be entirely possible to do everything with a single inlet. Indeed, before I saw your reply, this is pretty much exactly what I was thinking to say in response to Taylor’s delightful question.

As far as I can tell the ‘inconsistencies’ that you mention being in the library were there from the very earliest days. Miller Puckette’s 1988 paper on the Patcher already has the binary maths operators as they are today, with a hot left and a cold right inlet. Perhaps he himself could shed more light on why they chose to do things this way, but my own experience suggests that it was probably because he wasn’t really thinking about the semantics as such.

That is, it looks to me that the early development of Max had other, higher priorities. For example, a principle aim appears to have been to develop an object-oriented system that would respond to human input in real-time. In the mid- to late-1980s when all this was happening, this was still a rather brave and courageous undertaking, if not an entirely novel one.

The Patcher itself was rather more novel. Here, using the wonderful graphics tools that were coming available in computers such as the NeXT cube, Max had a user interface that was reminiscent of what the likes of Wendy Carlos or Keith Emerson had been doing with their mighty modular synths. But now, in software (with perhaps a bit of hardware accelerator assistance), you could take a set of objects and ‘patch’ them together using what appeared to be little patch cables.

I suppose that what I am trying to say—in, I fear, a rambling, incoherent way—is that I have always presumed that the reason the semantics of Max and its standard object library are not entirely logical and consistent is simply that they were never meant to be so. The idea was to build a system that had something of the ‘look and feel’ of a modular synth, to build something where musicians who did not see themselves as programmers could interact with their computer and, in doing so, could create patches in real time (to say nothing of the ability to save and reload these often complex patches at the click of a button, something those poor folks with their mighty modular synths and real patch cables were very much unable to do).

Returning to Taylor’s question, yes, I do believe that one could undoubtedly make a useable system such as you describe, one with almost all inlets being ‘hot’. Useable, yes; good or efficient, I am not so sure. I suspect that you would be more likely to want ‘cold’ inlets everywhere except when you receive a ‘bang’ message, in which case the inlet acts ‘hot’. The difficulty as I see it, and without having really thought about it carefully, is that the system you describe—all ‘hot’ inlets—would seem to have a great many spurious messages flying about. You could end up spending a great deal of your time and energy simply trying to stop some of the messages in their tracks—or at least to delay them until just the right moment. This was the meaning of my ‘more (or at least differently) convoluted’ comment in my first reply above; pretty much any system you come up with will makes some things easier to do and other things more difficult.

At the end of the day, Max is what it is. Like the standard library of any other language that has been used over many years and has had that standard library added to and extended (e.g., Python comes to mind), Max’s standard collection of objects certainly has its inconsistencies and idiosyncrasies. As Robin so rightly points out, there are any number of standard objects that violate Max’s own conventions. Also as Robin correctly suggests, cold inlets provide what is in essence syntactic sugar for having to otherwise build and send more complex messages to a single inlet. Indeed, even the seemingly non-message messages—bangs, ints and floats, for example—are in some sense really just syntactic sugar for messages that begin with the word bang, int or float followed by zero or more arguments. (And if you truly want a fun exercise, ponder the semantics of a Max object’s possibly multiple outlets, with some of these outlets sending messages at different times. Yeesh.)

To fully understand the semantics of any language, whether human or computer, you need to consider both the synchronic aspects (i.e., how it works now) and the diachronic aspects (i.e., how it evolved to be this way). Ultimately, though, it is the synchronic aspects that seem to matter most if you are trying to use a language as it is. And the best way that I know of to understand Max’s synchronic semantics (and indeed, MSP’s and Jitter’s, although these are another matter entirely) is to look at the SDK documentation. By understanding and appreciating the mechanisms that underlie the semantics, you are better placed to understand and appreciate the semantics themselves. Are other semantic models possible? Yes, of course. The question is whether having these different semantics makes things all that much simpler in the domain for which the language is intended. In language design you make choices and then deal with them, foibles and all.

The bottom line is that Max (including MSP and Jitter) was conceived to be an object-oriented, visual, real-time controllable language with an aim of trying to bridge the gap between being a programmer and being an artist-musician. Some of the choices made in this development were, it seems, intended to make things more intuitive for the artist-musician at the expense of being fully intuitive to the programmer. Whether or not this aim was successful or not is a matter of opinion, but it is clear that by its sheer longevity alone, Max does seem to work rather well for both the tasks and the people that it was designed for. Give it time, get to know it and perhaps even come to love it, foibles and all.

(Many apologies for this being so long. In the infamous words of Blaise Pascal, I lacked the time to make it short. I do feel compelled to add, however, that if you would like to see one possible future—and indeed, present—direction for the artist-musician cum programmer, have a look at Extempore, a system for live coding and ‘cyberphysical programming’ by Andrew Sorensen (with many contributions by Ben Swift) and currently under development down under in Queensland. I especially love Andrew’s YouTube video from Codemania 2015 entitled ‘A Progammer’s Guide To Western Music’. Mind-blowing as well as mind-expanding.)

taylor's icon

it would, in theory, be entirely possible to do everything with a single inlet.

What would my adding-two-numbers example (above) look like if everything had just one inlet?

ecuk's icon

You would end up with something akin to Reverse Polish Notation. You would first send a message to set one of the two parameters, then a second message to set the other, and finally a third, bang message to perform the computation and emit the result (as a new message) out of the outlet. The first two messages would act much like being sent to cold inlets in that nothing would be emitted; only the bang afterward would trigger the computation and the output.

This, to me, is one of the things that makes Max’s message model somewhat non-intuitive. It conflates the second and third messages into a single message to a ‘hot’ inlet, which both sets one of the parameters and triggers the computation.

Does that make sense?

ecuk's icon

In essence, this goes back to my earlier comments about Max being inherently object-oriented because objects contain local state. You might think of the Max ‘+’ object as not really adding two numbers. It is instead adding the second operand, which is stored as part of the local state, to whatever comes in the hot left inlet. By sending a number into the cold right inlet (or equivalently, a set message for this operand into the left), you are in effect creating an ‘add y’ object. When you later send a message to the left inlet with the ‘x’ that you want to add to this ‘y’, you trigger the computation of ‘x + y’. This, at least, is one way you might think of it.

If Max objects had only a single inlet, the language would be more pure but far less practical.

Roman Thilenius's icon

yeah robin ist right, it is not su much abourt right to left order, it is more about the most simple case of having only one input and therefore the importance of the "first" or "main" input.

Roman Thilenius's icon

What would my adding-two-numbers example (above) look like if everything had just one inlet?

"53 40", which already works.

but it could as well be "set operator 40" followed by "53"

btw i bet that in practice sending "40" to the first inlet of [+ 53] is used more often than having a variable operator.

ecuk's icon

For what it’s worth, the ‘add y’ thing I mentioned above is generally known as currying, named for Haskell Curry and his use of this concept in combinatory logic. (He is also who the Haskell programming language is named for.)

As for the comment by Roman that in practice sending ‘40’ to the first inlet of ‘+ 53’ is used more often than having a variable operator—by which I presume he means changing the ‘+’ to some other operator—in Max the object itself is instantiated (from a so-called ‘new’ object that in essence exists only until you type in an object name and instantiate something else in its place, in this case a ‘+’ object) and afterward the operator itself cannot be changed without instantiating a fresh object in its place, destroying the existing object. The operator is pretty much the very identity of the object. In some other object-oriented languages (e.g., C++) the operator would be, roughly speaking, the class of the object.

Robin Parmar's icon

What a great message and useful thread.

I recall my first encounter with the NeXT programming environment and how it blew my mind. Later I discovered some other rarefied visual languages for embedded system design. Compared to these, Max is indeed quite simply and, in many ways, not so well thought out. On the other hand, it certainly has its charms.

Unfortunately the message system is not one of these. I think it's simply confusing how lists and messages and symbols are smushed together, into a fairly unworkable mess. The presence of bang as a separate data type I view as a deficiency, when a command with the same name would work as well... and be able to carry parameters.

I would love a rewrite of Max in the same way Python 3 broke with backwards compatibility. This won't happen, since the commercial imperative is too strong.

I will check out those references you provided.

Robin Parmar's icon

Oh, by the time I finished the above there were five more messages. A good sign. :-)

But, for clarity, I was responding to the long post by ECUK.

ecuk's icon

Yeah, I myself am rather fond of good ol’ Lisp and all of its many descendants. Lists in Lisp are trees, not just these strange abominations you get in Max. Even the attempt at Lisp-like lists in the Bach package is surprisingly difficult to work with.

As for Extempore, it is worth noting that it is based on the same LLVM technology that underlies Gen’s just-in-time compilation. Both work on the same principal that nowadays you don’t really need to have control-rate signals—which were at least in part used by Max Matthews for reasons of computational efficiency—and you can do everything at the signal/sample rate. Although Gen’s great strength is that it is integrated into the Max environment, Extempore is entirely open source and designed for live coding; Extempore’s timing model is also based around a wonderfully Lispy thing called temporal recursion. The future looks interesting.

Robin Parmar's icon

I never got my head around Lisp and consider those who did somewhere closer to godhood.

The future is indeed "interesting" (in the Chinese proverbial sense) but takes it's sweet time getting here. Mostly I see more and more present.

ecuk's icon

In that case, meaning that you never got your head around Lisp, be forewarned that Extempore is really two languages, both of which use Lisp-like syntax. The first is really just an embedded Scheme interpreter for the less computationally intensive things. The second is the LLVM-based language affectionately known as xtlang. (I believe that Andrew was struggling to come up with something to call it other than just Extempore, which got confusing because of there really being two languages involved.) Xtlang is semantically much more akin to C than to Lisp, despite having a syntax reminiscent of Lisp. You might think of the embedded Scheme as being akin to Max and xtlang as being akin to MSP.

Going completely off-topic, I am reminded of a comment in the Lisp 1.0 manual from the early 1960s that says, ‘The commas in writing S-expressions may be omitted. This is an accident.’ But for this wee bug in the early Lisp parser, you would have had to write (+, 3, 4) to add 3 and 4 instead of the much more practical (+ 3 4). I suspect that the early history of programming—and indeed much of its later history as well—would have been very different were it not for this fortunate little bug.

Anyway, I can highly recommend Friedman’s ‘The Little Schemer’ as a good introduction to Scheme and Lisp. Even better is Abelson and Sussman’s glorious ‘The Structure and Interpretation of Computer Programs’, affectionately known as just SICP, but this is perhaps rather more difficult to get one’s head around. And if you truly want to go hardcore, Barendregt’s ‘The Lambda Calculus: Its Syntax and Semantics’ remains the one book that I would want to have with me on a desert island.

ecuk's icon

Somewhat more on topic, I am reminded of another amazing book, ‘The Art of the Metaobject Protocol’ by Kiczales, des Rievières and Bobrow. Although it is heavily based in the land of Common Lisp, and pretty much assumes that you are sufficiently fluent to follow along, it remains perhaps the single best book there is about the many varieties of object-oriented programming and their equally varied semantics, including what is happening deep within Max.

Roman Thilenius's icon

ecuk of course your assumption is wrong, because i meant the "operand" and not the +. :)

however, they way how objects are created, the fact that you can rename them, or that there are objects which can be called by different names somehow is part of this discussion.

Robin Parmar's icon

I especially love Andrew’s YouTube video from Codemania 2015 entitled ‘A Progammer’s Guide To Western Music’. Mind-blowing as well as mind-expanding.)

I was excited to view this, but gave up after 15 minutes. First, the language is completely unappealing to me (yeah, Lisp). Pretty well the anti-Python, full of brackets and syntax.

Worse yet, the presentation was poor. It amounted to showing things without explanation, like a magic show. I had absolutely no idea what any of the syntax did or why he suddenly introduced new structures. Neither did it live up to the billing, since the only thing he said about Western music theory was that pitch classes are modulo 12.

But thanks for trying, nonetheless!

ecuk's icon

First, let me also thank Taylor for the original inspiration for what has been for me an interesting and thought-provoking discussion.

Second, Roman, yes I agree that in Max, as in pretty much any other real programming language, there are multiple ways you might go about doing something, in this case adding two numbers.

My comments have been more aimed at an exploration of why Max and its message system are the way they are. As I have said, or at least hinted at a couple times, one of the things that I truly like about Max is that Cycling ’74 have made available to us the SDK, the very same SDK that is used to create all of the 700 or so objects that exist in the standard library (not including the 200 or so Gen operators, which effectively bypass the SDK). Even though we do not have the source of these many standard objects, we can develop our own objects that take advantage of the very same underlying mechanisms.

Max is really two things in one. The first is the Max language itself, which is based around an interpreter for the ‘language’ and includes all of the message passing semantics. The other is the standard library of objects, which are built using the very same tools that we have available to us in the SDK.

I suspect that many of the ‘inconsistencies’ in the standard object library stem from the fact that, once the SDK included such things as hot and cold inlets, originally intended largely as convenient syntactic sugar, object developers were free to use them however they wanted. With this freedom comes the dreaded inconsistency, however, because objects can now have any number of inlets, and each of these inlets can be effectively hot or cold. What comes in are messages, and what the object decides to do with these messages, perhaps depending both on the ‘class’ of a message and on which inlet it comes in, is rather less constrained than what was (perhaps) originally intended.

This is also why I mentioned the semantic nightmare that exists at the outlets. With freedom comes the responsibility to conform to convention. But with freedom also comes the possibility to enrich the arsenal of objects with ones that would not have been nearly as easy—perhaps nigh unto impossible—to implement with only single inlets and outlets. Take for example something as complex as the filterdetail object. The fact that I can have my filterdetail object spew out all kinds of useful things simultaneously from separate outputs, without having to somehow ‘filter’ which messages are going where outside the object, is nothing short of wonderful.

The bottom line is that the Max ‘language’ prescribes the semantics of message passing, not the objects in the standard library themselves. Objects, including the binary maths objects, each have their own internal semantics, whether adhering to convention or not, that have to do with the object’s local state and its behavioural responses to incoming messages. These things are based in code that we mere users cannot see. But we can effectively see, through the SDK, the mechanisms at work in the language that exist independently of the standard library objects. And it is these ‘language’ mechanisms, and my own suspicions about the reasoning (or lack thereof) behind them, that I have been trying to explore.

ecuk's icon

Robin, naturally I was busy pontificating instead of paying attention to incoming messages…

Yeah, I can see how if you are more of a Python kind of person than a Lisp one, Extempore might not be the best fit. For what it’s worth, the presentation does improve as he moves along, and it isn’t until near the end that things get rather more interesting. But I agree, perhaps it would be better if he didn’t do everything with so little explanation. I suspect his intention was to demonstrate something of the possibilities of live coding more so than to teach the language. The documentation is much better for the latter.

And for what it’s worth, I find Python’s syntax, to say nothing of its semantics, pretty much unbearable. If you ever want true nightmares, take a good, hard look at the internals of CPython’s interpreter and byte-code compiler ;)