I do a lot of odds and ends in Max/MSP and Reaktor for work. Normally I do the more robust stuff in C, ObjC and Ruby.
They're "dataflow" languages, you have boxes that transform data, and you wire them together in the order you want the transformation to happen. Everything's graphical. It's designed to be easy enough that someone with no computer background could use it– a composer or synth programmer will learn it for a few days and then off they go.
I've noticed some things:
- Code sharing almost never happens. You can't email a snippet of your "patch" (a program) as text, you can't post it in a text box at stackoverflow, it's almost impossible to communicate with other people about what you're working on without emailing the binary document. When you send someone a patch to look at, you're doing a lot of "look to the left of this," and "look for the red box."
- Code reuse can be difficult because boxes generally aren't typed in any way, so interfaces are difficult to verify and document.
- ... This leads the dev environments to only be as good as their templates and default libraries. People prefer Reaktor to Max not because it's easier for developing, but because it comes with a bunch of really useful default synths and sampler instruments, which people will tweak slightly.
- It's very difficult to talk about the algorithm itself, you have to spend all your time orienting yourself. If the program you're building is a simple pipeline, it's easy to see what's happening, but if you have loops and divergences it becomes very hard to understand what's going on in the abstract.
- Data types are a hack. You end up having to have different color wires that carry different things, type-tagging of binary data is routine, and you often have to do conversions because the environment runs different data connections at different levels of service. Trial and error is usually required to see if a box responds to a message in the way you want; I can write correct C without having to run the code, I would never try that in Reaktor.
- Execution order is a hack. If you connect one output to two inputs, which input will process the output first? There's conventions: In Max: the rightmost box will act first, and your graph is traversed depth-first right-to-left (this rule introduces ambiguity when dataflow is fed back). There are also boxes/modules that can make execution order explicit in various ways. (Note that in most cases we don't care about execution order, and the implicit multithreading is quite nice.)
- Doing N of anything is a pain. In Max, It's easy to build a sampler that can play one sample. It's easy to build one that can play two. It's basically impossible to build a sampler that can play N, without using the textual scripting language (ha!) to dynamically rewrite your patch based on creation arguments.
If I have something thats useful, I'll often conceptualize stuff in Max and then rewrite it in C with CoreAudio, because I know the Max code is basically a dead end for its usefulness.