Part V
Chapter 19 - Creating Classes
In object oriented programming classes serve as the blueprint for objects, the genotype information that results in instances of phenotypes. Like a recipe for cookes. This can be extremely useful when creating data structures that have properties (parameters or variables) and have behaviours (methods or functions).
For further information on what a class is, then a good start is the Wikipedia:
// run this line in SuperCollider “open ‘http://en.wikipedia.org/wiki/Class_(computer_science)’“.unixCmd;
Creating Classes
A good introduction to writing classes are in the Help documentation
“Writing-Classes”.openHelpFile
So below we have two TestClasses, one that subclasses the first one (like the guitar is a subclass of a string instrument).
Save both classes in a document that could be called “TestClass.sc� and make sure it is saved in the classpath of SuperCollider (where it is compiled on recompilation or startup). On the Mac the classpath for third party classes is in ~Library/Application Support/SuperCollider/External.
TestClass {
classvar <>myvar; // classvariables
var <>addnr, >addnrSet, <addnrGet; // instance variables
// this is a normal constructor method
*new { arg argaddnr;
^super.new.initTest(argaddnr)
}
initTest { arg argaddnr;
addnr = argaddnr ? 3;
// do initiation here
}
calc {arg a, b;
var c;
c = a+b;
^c // return
}
}
TestClass2 : TestClass {
calc { arg a, b;
var c;
c = a * b + addnr;
^c;
}
setAddNr_ { arg newnr;
addnr = newnr;
}
getAddNr {
^addnr;
}
}
When the classes have been saved and compiled we can now test the class:
t = TestClass.new //
t.calc(3,4)
r = TestClass.new(9)
r.addnr
v = TestClass2.new
v.calc(3,4)
v.addnr_(55)
v.addnr // our new class
t.addnr // the other of course still is just 9
v.addnrSet = 33 // we can set this number (because of > (a setter) )
v.addnrSet_(33) // another way of setting a variable (same as = )
v.addnrGet = 33 // Wrong! we cannot set this number ( because it is a getter < )
// proper object orientated programming uses setter and getter methods
// (rather than accessing variables directly)
// here we use the setAddNr_ method to set our variable.
v.setAddNr_(333)
// and we can look at it:
v.addnr
// but should really look at it with the getter method we made:
v.getAddNr
You can see that the < > symbols in the class are so called setters (>) and getters (<), which, if specified in front of the property specification, can serve instead of methods that set the properties. Therefore you can equally write
v.addnr(44) // using a setter and v.addnrSet(44) // using a method
Another test class
SonicArtsClass {
var win, textfield, textfield2, rect; // get text but set text2
var name, <>profession; // a getter and setter variable
var friends;
*new { arg name, rect, color;
^super.new.initSAClass(name, rect, color);
}
initSAClass { arg argname, argrect, color;
var scramblebutton;
rect = argrect;
name = argname;
win = SCWindow(name, rect, resizable:false).front;
win.view.background_(color);
textfield = SCStaticText(win, Rect(10, (rect.height/2)-30, rect.width, 30));
textfield.string_("");
textfield.font_(Font("Helvetica-Bold", 24));
textfield2 = SCStaticText(win, Rect(10, (rect.height/2)+30, rect.width, 30));
textfield2.string_("");
textfield2.font_(Font("Helvetica-Bold", 14));
scramblebutton = SCButton(win, Rect(10,10, 200, 30))
.states_([
["change friends color",Color.black,Color.clear]]
)
.action_({
friends.do({arg friend; friend.changeColor(Color.rand)});
});
friends = List.new;
}
speak_{arg string;
textfield.string_(string);
}
speak2_{arg string;
textfield2.string_(string);
}
updateGUI {
win.refresh;
}
addFriend {arg friend;
friends.add(friend);
}
getName {
^name; // note the return symbol
}
setName_ {arg newname; // note the underscore used when you are setting
name = newname;
}
removeFriend {arg friend;
var friendindex;
friendindex = friends.indexOfEqual(friend);
friends.remove(friendindex);
}
showFriends {
var namesOfFriends;
namesOfFriends = List.new;
friends.do({arg friend; namesOfFriends.add(friend.getName)});
textfield2.string_(namesOfFriends.asString);
}
getFriends {
^friends
}
getFriendNames {
var namesOfFriends;
namesOfFriends = List.new;
friends.do({arg friend; namesOfFriends.add(friend.getName)});
^namesOfFriends;
}
changeColor {arg color;
win.view.background_(color);
win.update;
}
}
{language= JavaScript, line-numbers=off}
And here is some code to try the class
a = SonicArtsClass("john", Rect(50, 800, 300, 200), Color.red)
a.speak_("Hi! I'm John")
a.profession = "singer"
a.speak2_("I am a" + a.profession)
b = SonicArtsClass("george", Rect(350, 800, 300, 200), Color.blue)
b.speak_("Hi! I'm george")
b.profession = "bass player"
b.speak2_("I am a" + b.profession)
c = SonicArtsClass("paul", Rect(650, 800, 300, 200), Color.green)
c.speak_("Hi! I'm paul")
c.profession = "guitarist"
c.speak2_("I am a" + c.profession)
// let's fix the roles
b.profession = "guitarist"
b.speak2_("I am a" + b.profession)
c.profession = "bass player"
c.speak2_("I am a" + c.profession)
a.addFriend(b)
a.addFriend(c)
a.showFriends
b.showFriends
c.showFriends
b.addFriend(a)
b.addFriend(c)
b.showFriends // check his friends
// what if john wants to change his name?
a.setName_("ringo");
a.speak_("Hi! I'm"+a.getName)
// we can get the name like this
a.getName
// but not like this:
a.name
// however, we can get the profession like this
a.profession
// WHY?
// the reason is the < (get) and > (set) properties of the profession variable
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);
Chapter 21 - Live Coding
Not written yet . . .
Chapter 22 - Other clients
Not written yet . . .
Chapter 23 - Twitter code
Not written yet . . .