Functional DSP?

I second what @reFX said. it’s a matter of taste, but stickin’ to the gain example I’d rather have a gain object which contains its interpolator. I think that’s the easiest approach

It’s practically impossible to create any useful program that does not mutate some values (the processor’s internal registers at least).
So there’s not much insight to be gained by pointing out that underneath most functional programming paradigms that some type of mutation is actually taking place. That’s obvious. Claiming that cleaner, higher-level functional abstractions are merely ‘hiding’ mutation is missing the point. And about as useful as pointing out that "C++ is merely syntactic sugar over ‘C’ (which is merely syntactic sugar over assembly language) … so we should all stick to using assembly language… right?

2 Likes

Now you’re putting words in my mouth to disparage my argument. You’re literally using a straw-man argument.

Never did I claim we should use C or asm. You said that. Not me.

I just think it’s pointless to hide away the mutations. You’re essentially hiding away what is really happening to the detriment of readability and unfamiliar-with-functional-programming developers.

Let’s use a more complex example:

input -> reverb -> output

Now you want to make a copy of the reverb object for every sample? Because there are internal states in the reverb that mutate (e.g. LFOs for subtle pitch-modulations) but at different frequencies. Maybe some of these only need to mutate once every 64 samples?

Now the reverb, ideally, should also be comprised of 20? 30? separate objects, all which don’t mutate their internal state. So just by processing a single sample within the reverb, all these objects get copied, replacing their previous state and at the end, the whole reverb object gets replaced.

For me it’s pretty clear: functional programming doesn’t work well for audio DSP. There are too many stateful algorithms needed to create a useful audio program.

Even the simplest of examples (a simple gain) fail.

So now we’re looking at functional programming, but allow for mutating state, which is simply called “good C++ programming”. Avoid mutations, if you can, use constants as much as possible and avoid copying objects unnecessarily.

Well, only conceptually. The compiler’s free to do what it likes, so if you have a bunch of functions to update the reverb in some way (reverb -> reverb) the compiler might plausibly leave that object in the same place and just update the necessary fields (well, not in C++ but maybe Haskell/F#/Ocaml).

It’s clearly possible to write DSP stuff in a purely functional way, and I see no reason why people shouldn’t be allowed to if they find the code easier to reason about, and they find the performance acceptable.

Nothing is impossible. You can render the CGI of a modern MCU movie in 4K on a VIC20. It’s just that the supposed benefits don’t materialize in the real world or are too expensive (performance wise) to accept for any reasonable user.

The promise of not needing to worry about multiple threads, because you never mutate state and thus are safe, are simply not achievable with real world programs. With context-less toy programs, yes. With real world applications that are used by actual customers, it’s only possible in a very limited set of conditions, which are not very useful.

Everything is a balance. Trying to force a square in a round hole simply doesn’t work.

I suggest you guys take a bit more time to really look at the Faust approach. Papers here: http://www.grame.fr/publications
Playground here: https://faust.grame.fr/tools/editor/index.html
Kadenze course here:
https://blog.kadenze.com/announcements/stanford-presents-digital-audio-signal-processing-in-faust/
Compiler here: https://github.com/grame-cncm/faust

2 Likes