Why creating (new) classes requires recompiling the whole class library?

Is there some deep reason for this design decision? It seems to run counter to the general philosophy of using an interpreted (albeit byte compiled) and a “duck typed” language.

I’m guessing that having a “fixed” class system (fixed after the interpreter is booted) simplifies the bytecode set (no dynamic creation of classes via bytecode). Is that the main reason?

Another reason I can see is that could be that classes aren’t fully determined until the whole library class is parsed because the SC class language actually has some aspect-oriented programming features in the way that classes can be “statically hacked” by other compilation units (files) with

+ Class {
    newMethod {
    }
}

The SC class guide says that this feature was inspired by Objective-C Categories but the latter is actually a dynamic feature in Objective-C, i.e. in that context one can add new methods to a class already compiled without having access to its original source code. Objective-C also has a notion of “class extension” aka “anonymous category” that additionally allows data members to be added to a class, but this requires access to the source of the original/base class. As far as I can tell this latter Objective-C feature is not actually supported by SC “+ Class”. So, just having (non-anonymous) Categories doesn’t seem to be a “show stopper” for incremental class compilation…

So, to reiterate my question, what’s the main reason why the whole class library needs to be recompiled in SC, when a new class is added?

The short answer is, frankly, historical. That may disappoint but it really is that simple: James McCartney built it that way initially (probably to avoid the technical problems of incremental class loading), and to change it would require deep surgery on the interpreter (large investment of developer-hours, and high risk of subtle bugs), and in a lot of ways, the benefits are relatively minor compared to the risks.

Hit send early…

Of course it’s technically possible, just… hard.

There has actually been talk about interpreter improvements, but it’s like when I worked in support at a software company, and I saw how a couple of badly needed improvements took years to be implemented, because of the difficulty and risk. They had program managers to make the call, “we’re going to spend the money to do this right”… we have a do-ocracy. So I’m not holding my breath.

I’ve been using prototype-based programming for almost 15 years to create “soft” classes at runtime, btw… even wrote a chapter in the SC book about it… but AFAIK this has never caught on :man_shrugging:

hjh

1 Like

I’d like to hear more about this. Coming from JavaScript this seems pretty natural to me. Would your chapter in the SC book be the resource on this you’d recommend?

Do you mean in the 2011 MIT book? I vaguely remember you have chapter about your chucklib etc. in there, but I don’t recall it being about prototype-oriented programming, although it’s been a while since I’ve last looked at that book…

Looking back at it now I see there’s a three-page section in there about Proto, which I didn’t recall at all :embarrassed: Personally for me the whole Chuck style operator => business is a turnoff.

I have to admit I also had overlooked that, and I discovered @jamshark70’s Proto work only way after having developed my own prototyping quark. I had discovered Event Prototyping through Alberto DeCampo, and there is also a chapter called Object Modelling on SC book about his approach.

So, my entry point to prototyping was to store prototypes as global variables, which has a drawback as it becomes hard to maintain and reuse code. I then naturally created a ProtoDef class, which like James’ PR implements a global register to store prototypes by name. It makes it easier to import/export prototypes, and to implement composition and inheritance.
Then I thought it was quite lame to debug prototype issues, so I added a little error message that adds informations about which method of which ProtoDef failed.
Finally I though we were still missing init and super functions when working with prototypes, and I added those as well.

@jamshark70 and everyone interested: Isn’t it time to come up with a “standard” implementation of Prototyping facilities? I would be up to work for this!

2 Likes

Right, I should have said it was part of a larger discussion.

Then – simply don’t use it!

Proto is implemented in the ddwProto quark.

=> is implemented in the ddwChucklib quark.

It was intentional to decouple object modeling from storage – so that you can use whichever bits of it you want and ignore what you don’t like. Anyone can create a different storage strategy that accepts my-style Proto objects, no problem. There was never any requirement to use “my whole system.”

~~

One large example of inheritance using Protos is here (using => PR() for storage – to be honest, I’m having doubts about putting the object ID at the bottom myself, but not enough to rip the guts out and redo it just yet):

Or this one (the chucklib-livecode parser):

But in the most recent revision, I had to rewrite these as standard classes. The problem was that, unavoidably, one pseudo-method call on a Proto results in at least 3 stack frames. Parsing nested expressions produces deep stack traces as it is, and I ended up in some cases with infloops during error handling because the stack had gotten too big.

So that’s one caveat – but not a common situation. I’ve been using Protos for 14 years and this is the first time I had to rewrite some pseudo-classes because of stack depth.

hjh

2 Likes

Had meant to answer this… That’s a good idea. An RFC is probably the way to do that (because there are potentially a lot of stakeholders)… write up a proposal in RFC form, submit at GitHub - supercollider/rfcs and see where the discussion goes.

hjh

1 Like