You cannot create nor edit a class without recompiling the language... but what if you need that?
Object prototyping is a middle ground that lets you build class–like structures at runtime. It works by taking advantage of the class IdentityDictionary (often the subclass Event is used) and the special method Object: -doesNotUnderstand. While there are some oddities with this approach, it is extremely powerful.
Instance variable–like things can be created by just adding keys to an IdentityDictionary
Unlike normal classes, you can create new instance variables at runtime.
By assigning a function to a key, you can call that function as if it were a normal instance method.
However, the IdentityDictionary is passed into the function as the first argument. Usually, we call this argument self
as it performs a similar role to the keyword this
.
Functions: Variable Arguments also work.
There is no direct way to mimic a constructor, but this isn't a problem, as we can simply make a function that returns the IdentityDictionary.
These can be implemented inside the constructor function as normal variables, however, you don't add them to the dictionary.
There are two drawbacks over classes.
The first, is that any key, including 'methods', can be overridden at any time. Essentially, what makes IdentityDictionary powerful, also makes it fragile and easy to break. The only way to avoid this mistake is by being vigilant when adding keys to an object prototype.
The second drawback is that you cannot have a key with the same name as a method in Event or any parent class, otherwise the real class's method will be called, not the pseudo–method you have written.
As seen above, one expects the value 10
to be returned, but instead Object: -numChannels is called and the value 1
returned.
There are two ways to mitigate this. One, use longer keys or names in snake case rather than pascal case: that is names_written_like_this, ratherThanNamesWrittenLikeThis, as SuperCollider only uses the latter.
Two, assign outside of the brackets, where a warning will be generated.
This is implemented with IdentityDictionary: -'parent' and 'proto' variables. Technically, this can be used to do more than inheritance, but only inheritance is shown here.
This is done by assigning the parent
to another dictionary to lookup keys in.
This is similar to how Javascript and other prototyping languages work.