I’m glad this was mentioned, handling event chains is particularly hellish in graphical environments such as Logic or Max/MSP. It was one reason I stopped working with Max primitives and started wrapping all of the interesting stuff in externals.
A fundamental composition problem, for me, is that one may apply a mapping f(x) to a note on event (“x”), and then change the function to g(x) by the time the corresponding note off arrives. The result is a stuck note, because f(x-“On”) should be canceled by f(x-“Off”), not g(x-“Off”).
This will occur basically any time the mapping changes dynamically. So the generic mapping f(x) needs first to be composed with a simple utility function that repeats the input: [x f(x)], and another function that stores the result in a table and then retrieves f(x) the next time it receives an x-“Off” message.
[Note: This has been permanently broken in the Logic environment since day 1, up to and including Logic 9. I have no idea if it was fixed in Logic X. But the idea is, never change “transformations” on the fly or you will get stuck notes.]
A second issue is that the mapping itself, f(x), can at best only be pre-selected from a menu, and configured with parameters. To pre-select from a menu, the patch has to already have implementations of all of the possible mappings. There’s no hope in a live situation of someone making up a new mapping and simply passing it to a “composition engine” that defends against stuck notes (because nobody wants to think about, or is really capable of, that kind of “programming” in a live situation).
All this would have been so much easier with the idea of a patcher that takes not just data-events as inputs, but other patchers (with known I/O configurations), and contains therein meta-rules for a) hooking them up (the essence of “composition”) and b) routing incoming data events. Patchers would be dynamically fed to other patchers, as a special type of event – just like audio and Jitter buffers are special types of events. All patchers would thus have this basic capability, to be operators as well as functions. One would then specify the “actual function” and let everything else take care of itself.
This is why, I gave up on Max except as an externals wrapper when I created a real-time transformation tool that also plays with the timings of events (simple things, like reversing/looping/double-timing buffers). My solution was still kludgy but at least possible in C++ [and I got people annoyed on the Max forum for even using C++ and not doing an external in straight C which it seems the community prefers…]
Anyway, I have zero experience with Lua, but its idea of “closure” seems to allow for at least some of this: functions can take other functions as input and data to be routed into these functions is scoped appropriately.
Reference: https://www.lua.org/pil/6.1.html
Of course, none this is a new idea, and the FRP idea probably either contains this as well as goes way beyond (which I need to review more, I merely skimmed without a deep understanding). But it may be a hint as to what is possible within Lua itself, to at least avoid the unmaintainable kludgey horrors of getting the most basic stream processing to work in Max.
I wanted mostly to argue that the simple idea of closure is both badly needed and possible in graphical programming environments and would do wonders for their renewed use in “live coding” situations. I also suggested a way this could be done.