2009/11/25 Kassen
This is indeed actually a bug, not a feature! Static variables are not initialized correctly (and currently need to be explicited initialized outside the class definition). We hope to address this in the near future!
Ge,
I suspect you misread the exact issue under debate here. Consider;
class War { static int foo; static Gain mix; 3 => static int bar; }
foo is perfectly fine, I'm sure we all agree there. mix is a a problem and that line in that form won't fly; it needs to be initialised from outside of the class. We can get around that using something like this;
class War { static Gain @ mix; static fun void init() {new Gain @=> mix;} }
...that way we at least keep all of the definitions inside of the class and only need to call init() from outside. I think this is what you are refering to; this needs a fix and correct behaviour is quite clear.
The problem under debate is the "bar case". bar, as a member, will be initialised at the definition of the class, it just won't have the value 3 without a instance of the class. Maybe I'm misreading your post here, and you do see the setting of the value 3 as part of initialising bar in this case? As pointed out earlier, doing so is not without questions as we may also set the value using something like;
myFun() => static int bar; //or myStaticFun() => static int bar; // or myNonMemberFun() => static int bar;
...assuming all of those return a int. No problem arrises if all those do is return a int, but they may have side effects and might, in their definition, refer to non-build-in types, potentially leading to circular definitions and much more complicated demands on the order things are parsed in. Allowing this may lead to syntactically correct files that still can't be executed.
I think it may be fine if we only allow static member functions (of the given class) to set such values, but that would mean a extra syntactical constraint. That said; setting static constants would be very handy in -for example- determining the length of static UGen arrays for vm-wide routing without getting into magic numbers. Right now we can't do that and such numbers can't be defined as a part of the class they belong to without instantiating the class as well. This leads to some important numbers getting defined outside of the context in which they are important, which won't win any beauty awards; for that reason I sugest currently using something like my "init()" example above as that structure really helps when returning to code weeks later.
We can call the current situation a "bug" on the grounds of a line of code being only partially executed, but I don't think it's a simple bug where we can effortlessly say what should happen instead. One of my concerns is that we are very concerned with deterministic execution order in ChucK but that's no good if the execution order is very hard to predict.
Phew! :-)
Hey, it's a holiday here and I feel like rambling. Usually I edit posts to be as short as possible, but not today! Today I give thanks -- to words! Sorry, that's just how mailing lists work. Any dude can just come on by and dump a lot of words into a lot of inboxes whenever they feel like it. Like this. So the idiom I follow is the initialize-after one that's been mentioned a few times: class War { // declarations and instance initialization static int foo; static Gain @ mix; static int bar; } // static initialization 3 => War.bar; new Gain @=> War.mix; It's not intuitive to write (until you're used to it), but it is to read. It's clear that the static initialization happens once, right after class declaration. The static variables can be considered initialized anywhere in the class. There's no floating "War war;" to get things rolling. If it's a public class, it's likely that this is all that's in the file. If the compiler can get static initializers working, that'd be great, although having some statements execute once when the class is loaded and some when a class is instantiated is weirdly non-regular, and has issues like Kassen pointed out where it seems like you should be able to use functions depending on instance variables in static initializers due to code proximity and seeming scope. I think other compilers just check that kind of thing. That's where my rage in Java comes from, when a simple program grows to that breaking point where it's too big for a single, static main() method and I have to start making objects that Java will let me instantiate from that static safety zone that can't be inner classes because those aren't static, somehow, so I have to either make a second, non-static main or open a second file to create the classes I need as public, or fill my original class with a bunch of static methods and variables. Man, that had absolutely nothing to do with ChucK. Anyway, "static:" also suffers from this a little since to me it doesn't imply the one way "can-use" relationship between static and non-static initializers (non-static can use static, but not the other way around). Or maybe I'm a little too used to { } being the only characters that delimit scope. Unfortunately, that preference would lead to something like: class War { // class initialization int foo; Gain @ mix; 3 => int bar; instance { // instance initialization 4 => int baz; fun void screw_with_your_head() { <<< "screwing complete" >>>; } } } ... which even I don't like, but I think is illustrative and funny. Seems like a great way to make miserable the evenings of the live-coders and totally break every ChucK class ever written. Or you could pop everything left one brace level and almost be back to where we started, except with static declarations outside and before the class -- which is actually another way to handle static variables by the way, take note. Less typing to boot. int foo; new Gain @=> Gain @ mix; 3 => int bar; class War { 4 => int baz; } Well, have a good one! -- Tom Lieber http://AllTom.com/