Chapter 20 - Functional Programming
SuperCollider is an object oriented programming (OOP) language inspred by SmallTalk. In SmallTalk (and unlike languages such as C++ or Java) everything is an object, so it’s possible to create methods and subclass practically all data structures. Thus we find in SuperCollider the methods of .toLower for a string or a char (“HEY”.toLower and $A.toLower) or .neg or .cos for a number (10.neg and 2.cos). Here the actual number is an object that has methods (e.g. .neg).
We could for example create a .double method for SimpleNumber. We’d simply open the SimpleNumber class and create a method like this:
double { ^this * 2 }
But don’t do that. If you are creating your own methods and classes, keep them in your own Extensions folder, so the next time you update SuperCollider, you still have your classes and methods at hand.
However, there is another way of thinking and it’s different from OOP, a bit like the difference between Plato (reality is static ideal types) and Heracleitus or Buddha (reality is a flow). Let’s explore functional programming.
SuperCollider is also a functional programming language. You can program solely using the functional paradigm and we will look at the FP classes below.
Functional Programming
In functional programming (FP) the idea is not to create classes and instantiate objects that exist in the computer’s memory, responding to messages and calls. It’s not a world of things, but a world of movement, behaviour, events.
So it’s not “John.drives(work)| but rather a “drives - work - John”. The function is to drive, and it’s John who is going to work.
So the doubling of the number above would be a function:
double = {arg num; num*2}
In short, the idea is to avoid state and mutable objets, as those are typically the source of bugs and errors (often called side effects) in imperative and object orientated programming languages.
Functions as first class citizens
In a functional programming language, it is important to be able to send functions around into other functions as arguments, and if that’s possible, the language supports “functions as first class citizens”
~double = {arg num; num.value * 2 }
~double.value( {3+3 )
~square = {arg num; num.value * num.value }
~double.value( ~square.value({3}) )
Above, the result of the function ~square was passed to the function ~double
Recursion
Iteration or looping is typicaly done with recursion in functional programming. So instead of the C and Java:
fact = 1; for(int i=1; i>6; i++) { println(fact*i); }
Or normal SuperCollider:
var fact = 1; 5.do({arg i; (fact = fact*(i+1)).postln;})
The Scheme function for the above would be
(define factorial (lambda (n) (if (= n 0) 1 (* n (factorial (- n 1))))))
(factorial 5)
And a Python version could be written with some explanations like this:
def factorial(n) : print “entering factorial level (n) %i” % n if n == 1 : print “deepest level of recursion reached! n is now : %s” % n return 1 else : r = factorial(n - 1) x = n * r print “ n is: %s and r is: %s” % (n, r) return x
level = 5 print “top level function returns %i when n is %i” % (factorial(level), level)
The SuperCollider recursion would be something like
f = {arg n; “n is: “.post; n.postln; if(n==0, { “n is now zero”.postln; 1 }, { (n * f.value(n-1)).postln; }); };
f.value(5);