Day Four of 30 Days of Supercollider
Variables in Supercollider, not surprisingly, are rather special, compared to many other languages. I can count four rather distinct types of things that will hold a value:
- Regular vars
- Arguments
- Single-letter variables
- Environmental variables
Regular Variables
Let’s start with regular variables. These aren’t much different than variables you’d find in most other languages. You declare them with the var
keyword and the name of the variable, which should really be more than one character long and has about what you’d expect for legal identifier names, as far as I know. They should also start with a lowercase letter, as identifiers starting with capital letters indicate a class name.
You can optionally decide to assign the var a value when you create it, or you can do it later. Unassigned vars have a value of nil
. Vars are not typed, so you can reassign them with data of another type of you want.
(
var foo;
foo.postln; // nil
foo = "hello";
foo.postln; // hello
foo = 42;
foo.postln; // 42
var bar = "hello";
bar.postln; // hello
)
You must use the var
keyword before assigning a value to a variable. i.e. you can’t do something like the following. It will throw an error that foo
does not exist.
(
foo = 99;
)
Vars have scope, as you might expect. A var inside a function is scoped to that function and will have a different value than a var of the same name outside the function, as the following shows:
(
var foo = "apple";
var func = {
var foo = "banana";
postln("in function: " + foo); // banana
};
func.value;
postln("outside function: " + foo); // apple
)
Also, vars declared in one region are scoped to that region and will not be available in other regions. Example:
(
var name = "keith";
name.postln; // keith
)
(
name.postln; // error, name is not defined.
)
Finally, vars must be declared before any statements are executed in a given function or region. This will fail:
(
"hello".postln;
var foo = "hi";
)
But if you switch the order of the two lines, it will be fine.
Arguments
We already looked at arguments when we covered functions. As far as I know, they have all the same rules as regular vars, but they need to be declared before vars or any other code in a function. Oddly, you can declare args outside of functions and they seem to work pretty much as regular variables. So most likely they are pretty much the same under the hood.
(
arg age = 90;
age.postln; // 90
)
Single-Letter Variables
Earlier I said that regular variables should be more than one character. The reason for that is that single-letter variables are known as global variables. Global variables a
to z
already exist and can be used without the var
keyword. And as their name suggests, they are available across regions, functions, any scope.
(
f = {
a = "hello world";
}
)
(
a.postln; // nil
f.value;
a.postln; // hello world
)
Evaluating the first region will assign a function to global variable f
. Inside that function, global variable a
is assigned a value.
Evaluating the second region calls postln
on global variable a
, which should not have a value yet, so it shows nil
. It then calls value
on the function stored in f
. Although that function was declared in another region, it is still available here because f
is global.
After the function is run, we postln
the variable a
again. Now it has the value assigned to it in the function.
This globality even works across files. If you open one file and write to a single-letter global variable, you can open a new file and read from it.
Now we’ve all been taught that global variables are bad. But in most cases, when you’re coding in Supercollider, you’re not doing hard computer science. You’re just being creative. So I think it’s OK to relax a bit. Since you’ll often be defining functions in one region and using them in another region, global vars become kind of necessary in many cases.
Of course, if you are making a plugin or some kind of reusable code library, avoiding globals is still a very smart idea.
One more vital warning here. You should avoid using the global variable s
in your own code. This has been assigned as the current server. So you can do things like s.boot
, s.reboot
, s.stop
. There’s nothing special about s
other than it’s already being used. If you really, really think you need to use s
, then at least reassign the server to some other variable.
(
z = s;
s = "foo";
z.boot;
)
Environmental Variables
Environmental variables are similar to global variables in functionality, but can be even more useful because you aren’t limited to a single character. Your variable name can actually be useful.
Environmental… ok, I’m just going to call them env vars. Env vars always begin with a tilde and do not need the var
keyword. Otherwise they work pretty much like vars and global vars. You can use them in any scope. This is the same example we saw before, redone to use env vars.
(
~magic = {
~message = "hello world";
}
)
(
~message.postln; // nil
~magic.value;
~message.postln; // hello world
)
Technically though, env vars are different than global vars. They are scoped to the current environment. And really, the code ~foo = "hello";
is an alias for currentEnvironment.put(\foo, "hello");
I’m not going to go too deep into environments, but they are basically namespaces. You can create new environments, push them and pop them off a stack of other environments, etc. But until you’re actually doing things at that level, env vars are probably safe to consider essentially global. If there’s an edge case, I’m sure someone will bring it up.
This will be an index of the articles I post about Supercollider. Warning: I don’t know a lot about Supercollider yet. This will be a journal of my discoveries as much as anything else. I pro…