Splitting off from:
For fun, I just counted up how many lines of code in my quarks are in .sc
class files, and how many are in .scd
.
- .sc = 26232
- .scd = 23419
Now… not everything in .scd will be a Proto, but it would still have to be, oh, 30-35% (?) of my public code being “soft”-defined.
I noticed in the last couple of years the opinion being expressed more frequently that “object prototyping doesn’t work.” Weeelll… I dunno… it’s been working quite well for me, for a couple of decades.
IMO whether to use prototyping or not depends on individual inclination, and the situation.
I started prototyping because, in 2004, I was losing a lot of time while preparing a concert piece because my object design required me to recompile the class library and reload the entire environment, just to change one small thing. That was an awful experience. I decided at that time to make all of my creative code “soft” – to change something compositionally, it would save a lot of time to be able to delete one process and re-create only that process, rather than having to blow away and rebuild the entire environment.
Are there trade-offs with prototypes? Yes. Are they worth it when, for instance, I need to debug one of the generator objects in my live coding environment?
- If those generators were hard classes: Change something, recompile the class library, reload the LC environment, step through a few init lines in a testing script, test.
- Actual process: Change something, reevaluate the generators.scd file, reload the one line LC pattern, play the process.
In that context, I find Protos are slightly less readable but significantly more flexible, so I choose to continue to use them.
By contrast, the parser in my LC environment needs to be stable and reliable: malleability is a negative. So those are hard classes.
Criticism of object prototyping usage is usually:
- You have to avoid methods defined in Object and Environment.
- ddwProto offers a partial solution by “has-a” wrapping Environment, and not implementing many of the Environment methods.
- Beyond that: In practice, I have only very, very seldom gotten tripped up by this. It’s really not that big of a deal.
self
. I’ve vocally disagreed with this design decision before. An object’s internal code is normally accessing local member variables.self
inverts that: in “vanilla” object prototyping, it’s easier to access globals than it is to access locals. Of course that’s unpleasant! One reason why I’ve gotten good results out of prototyping is that I just don’t do it that way. My Proto class prioritizes access to local variables, and a whole lot of readability problems just disappear.
DietCVUnitShaperLoader {
*initClass {
StartUp.add {
(this.filenameSymbol.asString.dirname +/+ "unit-shapers.scd").load;
}
}
}
Aaanyway… just a reminder that mileage may vary, and one person’s opinion may not apply to your needs.
hjh
PS To make it more concrete what I mean about local vars:
// "vanilla“ prototyping
(
~collatz = (
n: rrand(10000, 50000),
// cannot use `next`
nextValue: { |self|
if(self.n.odd) {
self.n = self.n * 3 + 1
} {
self.n = self.n div: 2
};
}
);
)
~collatz.nextValue;
// Proto
(
~collatz = Proto {
~n = rrand(10000, 50000);
// Proto implements 'next' to redirect to ~next
~next = {
if(~n.odd) {
~n = ~n * 3 + 1
} {
~n = ~n div: 2
};
};
};
)
~collatz.next;