I can hardly call this a bug, since people who go around casting classes get what they deserve, but it's unexpected behavior nonetheless. Consider the following -- the salient point is that General is a super class, Specific extends General. =================== class General { fun void announce() { <<< "I am general:", this.toString() >>>; } } class Specific extends General { fun void announce() { <<< "I am specific:", this.toString() >>>; } } fun General create(int type) { // factory method return (type==0)?(new General):((new Specific) $ General); } fun void process(General obj) { <<< "processing general object:", obj.toString() >>>; } fun void process(Specific obj) { <<< "processing specific object:", obj.toString() >>>; } General @ obj; create(0) @=> obj; // [1] create a General object obj.announce(); // [2] calls General.announce() process(obj); // [3] calls process(General obj) create(1) @=> obj; // [4] create a Specific object obj.announce(); // [5] calls Specific.announce() process(obj); // [6] calls process(General obj) process(obj $ Specific); // [7] calls process(General obj) =================== which produces the following (I've annotated the line numbers): =================== r% chuck classtest.ck [2] I am general: General:5df960 [3] processing general object: General:5df960 [5] I am specific: Specific:5de400 [6] processing general object: Specific:5de400 [7] processing general object: Specific:5de400 =================== Lines [1], [2], [3] do what you expect: creates a general object, calls the General.announce() method, and calls the process() function which has the <General obj> signature. So far so good. Lines [4], [5] also do what's expected: creates a Specific object and calls the Specific.announce() method. Line [6] is up for grabs. I half expected it to call the version of process() with the <Specific obj> signature (since it called the Specific.announce() method at step [5]). On the other hand, obj was declared as a General class, so it doesn't surprise me that it called the version of process() with the <General obj> signature. Line [7] is what bugs me: obj is cast to the Specific class, and the ChucK compiler _knows_ this at compile time. Yet it calls the process(General obj) function, NOT the process(Specific obj) function. If ChucK uses run-time checking for signature discrimination, then [6] should call process(Specific obj). But it uses compile time checking, right? So why doesn't it call process(Specific obj) in step [7]? I promise not to lose too much sleep over this. If someone familiar with ChucK's type system wants to report this as a bug, be my guest. I'm just happy to find that ChucK has a smidgen of run-time type discrimination (as evidenced at [5]). - Rob
Robert;
Line [6] is up for grabs. I half expected it to call the version of process() with the <Specific obj> signature (since it called the Specific.announce() method at step [5]). On the other hand, obj was declared as a General class, so it doesn't surprise me that it called the version of process() with the <General obj> signature.
Yes, I agree with ChucK here; "obj" is defined as a "General" and correctly taken as a argument to the relevant function. It is *also* a Specific at this point, I believe, and could be cast to Specific at which point any member functions that Specific might have (that General would lack) should be available. In this case all Specifics are also Generals as well, by definition, and this one was defined as a General and thus treated as one. much like a cow, treated as a example of a herbivore, will still be a cow.
Line [7] is what bugs me: obj is cast to the Specific class, and the ChucK compiler _knows_ this at compile time. Yet it calls the process(General obj) function, NOT the process(Specific obj) function.
I found this quite baffling as well. Then I switched the order that the two versions of the overloaded function are defined in around and got; [chuck](VM): sporking incoming shred: 1 (unnamed1)... I am general: General:8ae4c10 processing general object: General:8ae4c10 I am specific: Specific:8add058 processing general object: Specific:8add058 processing specific object: Specific:8add058 I believe that to be the correct and expected behaviour. It seems that when multiple versions of a overloaded function could be used with a given object ChucK will take the top one of the options. I find that defensible, though it would probably be preferable if the default would be the least general case (so taking Sitar before STKInstrument, before UGen, before Object). Not a bug, I'd say, but a undocumented element of the syntax that might give unexpected results. It may need documentation and could perhaps use some polishing though. So; I learned something new, thanks! :-) Yours, Kas.
On 30 Sep 2009, at 17:33, Kassen wrote:
it would probably be preferable if the default would be the least general case (so taking Sitar before STKInstrument, before UGen, before Object).
I'll change your "probably be" to "": it WOULD be preferable to call the most specific version of an overloaded function. ChucK's class system has single, straight-line inheritance, so the "most specific" is always well defined. The current scheme -- using the (lexically) first function definition -- seems fraught with astonishment. Thanks for digging into this. - Rob
Robert;
I'll change your "probably be" to "": it WOULD be preferable to call the most specific version of an overloaded function. ChucK's class system has single, straight-line inheritance, so the "most specific" is always well defined.
You are probably ;-) right, I was hesitant to make a strong statement here as I wasn't sure this would hold true in all cases. There might be exceptions though now that I have thought about it for a while I have to admit I still haven't found any. I'm hesitant to make strong claims in fields like this because the situation might stretch to near infinite complexity and unforeseen possibilities may pop up.
The current scheme -- using the (lexically) first function definition -- seems fraught with astonishment.
I don't think the situation is terrible; it doesn't crash, correct behaviour can be had and the results seem deterministic so far. It can also be improved without breaking old code in nearly all cases. My main issue is the lack of documentation. The manual just notes; *overloading*
Overloading a function allows functions with the same name to be defined with different arguments. The function must be written in separate instances to handle the input, and the return type must agree.
...and gives some examples. I'd say it would also be useful to point out exactly what overloading is, because the above will make zero sense to first time programmers, why we would use it and note things like the above. Generally the order in which functions and classes are defined in the code shouldn't matter, at least the specs don't make any mention of this mattering, yet it does. The other cases that I know of are just plain bugs. To me this seems like a case where the passes of the parser over the code don't exactly reflect what should be done and more should be moved to a preliminary pass that scans for definitions and files them appropriately for dealing with the rest of the code.
Thanks for digging into this.
My pleasure; it was a good puzzle and the result taught us something new. Yours, Kas.
2009/10/1 Kassen
The current scheme -- using the (lexically) first function definition -- seems fraught with astonishment.
I don't think the situation is terrible;
I do. This is one of the things that really gets up my nose in ChucK.
it doesn't crash,
Oh. Good.
correct behaviour can be had
Are you familiar with the concept of Turing Equivalence? Once a language reaches a certain expressive power, it's not about whether or not you can get things to happen, it's about how much pain you ut the programmer through.
My main issue is the lack of documentation. The manual just notes;
overloading Overloading a function allows functions with the same name to be defined with different arguments. The function must be written in separate instances to handle the input, and the return type must agree.
...and gives some examples. I'd say it would also be useful to point out exactly what overloading is, because the above will make zero sense to first time programmers, why we would use it and note things like the above.
And the above behavior essentially breaks the main utility of behavioral inheritance: that you can cause more-specific behaviors to occur in a more-general context. Really, this is just wrong.
mattering, yet it does. The other cases that I know of are just plain bugs.
Then perhaps so is this.
To me this seems like a case where the passes of the parser over the code don't exactly reflect what should be done and more should be moved to a preliminary pass that scans for definitions and files them appropriately for dealing with the rest of the code.
So it's a bug that you know how to fix? david rush -- GPG Public key at http://cyber-rush.org/drr/gpg-public-key.txt
Greetings!
... process(obj $ Specific); // [7] calls process(General obj) =================== which produces the following (I've annotated the line numbers): =================== ... [7] processing general object: Specific:5de400 ===================
...
Line [7] is what bugs me: obj is cast to the Specific class, and the ChucK compiler _knows_ this at compile time. Yet it calls the process(General obj) function, NOT the process(Specific obj) function.
Yes, it would seem something is definitely amiss here - will look into this bug more deeply! Thanks for the heads-up! Ge!
Ge Wang wrote:
Yes, it would seem something is definitely amiss here - will look into this bug more deeply! Thanks for the heads-up!
Another head-scratcher that I've run into on more than one occasion: the order of class definition within a .ck file shouldn't matter, or if it does matter, the error messages need work. If in the file ArraySet.ck (link below), the order is public class ArraySet extends Set { public Iterator iterator() { ArraySetIterator iterator; ... return iterator; } } class ArraySetIterator extends Iterator { ... } I receive this odd error message: [ArraySet.ck]:line(84): function 'iterator@0@Set' was defined with return type 'Iterator' -- but returning type 'ArraySetIterator' [ArraySet.ck]: ...in function 'iterator' The compiler appears to know about the type ArraySetIterator but doesn't know its type hierarchy. If the classes are defined in the opposite order class ArraySetIterator extends Iterator { ... } public class ArraySet extends Set { ... } everything is ok. michael http://github.com/heuermh/lick/blob/6b06cf6955fa0f85c9e25c2e663f6a5af1fc0247...
David;
I do. This is one of the things that really gets up my nose in ChucK.
Fair enough, I see your point, though I do wonder why you didn't report it then.
So it's a bug that you know how to fix?
Well, no, I was trying to figure out where and how things were going wrong. When there are a few bugs that act along the same lines they might -together- describe a structural issue. It wasn't the most profound of statements, I'll admit. Yours, Kas.
[ArraySet.ck]:line(84): function 'iterator@0@Set' was defined with
return type 'Iterator' -- but returning type 'ArraySetIterator'
[ArraySet.ck]: ...in function 'iterator'
My guess is that the compiler does NOT know about ArraySetIterator,
but is rather just parroting the line "ArraySetIterator iterator," and
doesn't find out about it until later. This is where C would use class
prototypes (or whatever they're called) in a header file, but I'm not
sure if ChucK can do that.
(Kas & David): I think it's a priority issue here--when there's a
fairly simple workaround, the limited development resources (Ge) are
probably better spent on things like GC, and these things can be
referenced on the list. I think the best solution for things like this
would be continual updating of the wiki, because the wiki does have
the potential to be a fantastic document, given some community
involvement.
Andrew
2009/10/1 Kassen
David;
I do. This is one of the things that really gets up my nose in ChucK.
Fair enough, I see your point, though I do wonder why you didn't report it then.
So it's a bug that you know how to fix?
Well, no, I was trying to figure out where and how things were going wrong. When there are a few bugs that act along the same lines they might -together- describe a structural issue. It wasn't the most profound of statements, I'll admit.
Yours, Kas.
_______________________________________________ chuck-users mailing list chuck-users@lists.cs.princeton.edu https://lists.cs.princeton.edu/mailman/listinfo/chuck-users
participants (6)
-
Andrew C. Smith
-
David Rush
-
Ge Wang
-
Kassen
-
Michael Heuer
-
Robert Poor