[Grace-core] Semantics of object/classes

James Noble kjx at ecs.vuw.ac.nz
Wed Aug 8 15:30:01 PDT 2012


> The problem that I see with both your description and Kim's is that they are both written interns of code mangling.

Hmm, perhaps, but also because I was trying to be more specific about what happens when objects using inheritance are created. 

>  James writes, for example, that the declarations of the superclass are added to the object being constructed. 

So I'd be (almost) happy to read "declarations" there as "slots" - meaning parts of objects.
Except of course, in this interpretation, the most obvious interpretation is parts of objects that haven't yet been created.

It should be possible to say something like
 - when each object constructor starts running, the "object being created" has all the defs and vars and methods it will have
    (I was using "declarations" as a shorthand for "defs and vars and methods")
 - then starting with the topmost object constructor or class, the code in each constructor is run to initialise the defs & vars and do whatever else

Given that the object constructor model explicitly has code that is run as part of invoking an object constructor,
I don't see how to describe this without talking about running that code!

Your wording says:

> The short story is very simple: all of the methods of the superObject, car in the above example, are added to the object being constructed by the object constructor

I changed it to:

> The short story is very simple: all of the declarations of the super class, car in the above example, are added to the object being constructed by the object constructor that inherits from that super class.


swapping "declarations" for "methods" and  "object constructor that inherits from that super class" for "object constructor" because I thought your text wasn't clear about which object constructor was which.

Yes, especially in our model with a unified self, its easiest to talk about the object constructors of the super-part-objects
rather than the objects themselves.  But there's probably another way to put it --- we could pretty much re-write it in your
style, in terms of objects, and then just add in a couple of riders - very roughly:

> The first line of an object constructor or class can be the statement `inherits superClass` , where superClass is a method request that constructs a new object 


> In top-level code inside object constructors, in field initializers, and in any annotations etc, the keyword self always means the new self being created by the bottom-most object constructor, that is the object constructor that's doing the inheritance, bicycle in our example.


I still don't know if you accept this second point! 


>  I don't want to talk to my students about code: I want total to them about objects.   That was a large part of the motivation, for me, in doing away with classes as chunks of code, and instead making them objects with a method that creates an (instance) object.


sure. Although we won't be as close to Smalltalk or Self given our text-based IDEs & program representations -
but then there are very good reasons not to be image based!

> So in James' and Kim's formulations, 'Car.new' in the clause 'inherits Car.new' doesn't mean the object that car.new means everywhere else in the program: it means the chunk of code that defines car.new.   

yes, in practice - although I'm still hopeful we can finesse this. Talking on Tuesday, we discussed another solution (I think related to Marco's placeholders) where the inherits clause does the concatenation "backwards" so 

> object  { D... }   /// makes a new object described by D...

but 

>  object { inherits X D... }  //  "inserts" all parts of the object described by D into X

the last one is backwards from the way we've described it so far,
which seems to have been

>  object { inherits X D... }  //  "inserts" all parts object X into new object described by D 


(and I note I'm cheating with "described by D" because really at some point the code D must run.

> I went to sleep last night worrying about this problem (in the sense of a dog worrying a bone) and realized that the above difference is not one of descriptive stye, abut a fundamental difference in the way that we think about, and probably teach, programming.   It's the difference that I see between Smalltalkers who really "get" oo and Java programmers who don't: the Smalltalkers talk about objects, and the Java programmers talk about code. 
> 
> So it's really important to me that we can describe inheritance in terms of objects.

how does the "backwards" inheritance description fit that criterion?


> This morning I thought of another compromise.  Suppose that every object has special initially (and finally) method, defined with a  special syntax
> 
> 	object {
> 		def 
> 		method ...
> 		...
> 		initially { ... }
> 		finally { ... }
> 	}
> 
> Apart from the special syntax, these are like other methods, in that they are represented in the running object, they can be called explicitly if need be, and they are inherited; a sub-object can request its super-object's initially method by writing super.initially.  What's special about them is that
> 
> 	- an object's initially method is requested automatically after the object's constructor has completed, 
> 	   but _before_ any other method is requested.
> 	- an object's finally method is requested automatically after all other method requests on an object 
> 	   have completed (i.e., in implementation terms, sometime before it's garbage collected)
> 
> I think that these will solve Kim's issue of automatically initializing the variables of a graphical object.  They are just like Smalltalk's initialize method and Java's constructors, but deal with finalization too, which we will have to address sometime.   

I don't think this works - if they did, you could solve the problem by sending that message 
in the "unfolded nested object" form of a class declaration.  The problem is that each 
super-part-object that you're inheriting from will get repeatedly initialized once for each subclass.

The real problem is - in the typical inheritance use case - you want to initialise the whole resulting object once and only once, with self (and thus blocks) always bound to that object. 

> They don't allow me to change the value of a def'd field, of course, because once a field has been def'd, it can't be changed.  Is this a big problem.

It pretty much *is* the problem, and why Smalltalk (and Self!) solutions don't work here:
We want to be able to initialize all defs per object.
(see comments below too)



>> 
>> I don't know if this will can what Andrew wants his inheriting superclasses to do -
>> in fact, Andrew, can you send a short summary of the problem that needs fixing?
> 
> So let me be concrete about the problem.  I have a class aTestCase with a  constructor method class forMethod(m).  The idea is that when a programmer writes tests, she makes then parameterless methods called testxxx in a subclass of aTestCase.  So, for example, I have tried writing
> 
> class aSetTest.forMethod(m) {
>    inherits aTestCase.forMethod(m)
> 
>    var emptySet // :Set<Number>
>    var set23   // :Set<Number>
> 
>    method setup is override {
>        super.setup
>        emptySet := aSet.new
>        set23 := aSet.new
>        set23.add(2)
>        set23.add(3)
>    }
> 
>    method testEmpty is public {
>        assert (emptySet.size == 0) description ("emptySet is not empty!")
>        deny (emptySet.contains(2)) description ("emptySet contains 2!")
>    }
> 
>    method testNonEmpty is public {
>        assert(set23.size == 2)
>        assert(set23.contains(2))
>        assert(set23.contains(3))
>    }
> }
> 
> The problem is that in order to inherit from aTestCase, I MUST redefine `forMethod()`.  If I don't, I will get an instance of aTestCase, not an instance of aSetTest.   In Smalltalk I could just inherit `forMethod`; its body would say `self new` and that would generate an instance of the current self, which would be  aSetTest.   In Java, of course, I can't inherit from classes at all, so I'm just screwed.

Right. Now certainly if you're writing expanded nested objects instead of classes, you can *almost* do the Smalltalk
thing in Grace: have a no-args factory method called "new" or "basicNew" in each "factory object" (whehter created
by  class declaration, or by writing out the expanded nested objects)  that creates an object, and then 
put additional "secondary factory methods" like "forMethod" wherever you want them, and implement
all the secondary factory methods by calling your "new" or "basicNew".

This works absolutely fine --- but cannot initialize per-instance defs.  
Smalltalk & Self basically don't have per-instance defs. 

> In this example the re-definition of "forMethod" isn't terrible.  But suppose that I have four different constructor methods.  How do I inherit all of them?  I can write nested object constructors, where the classes inherit from each other in addition to the objects inheriting form each other, but I still have to make one of the constructors "primary", and write the other three in terms of the primary constructor, and then redefine the primary constructor.  So more of the implementation of the superclass shows through than it does in Smalltalk, where all of the constructors are inherited.  I suppose that in Smalltalk, the thing that I have to re-define in the subclass is the instance variable setting method, and this is perhaps the moral equivalent of the Grace constructor method.
> 
> Having some kind of "class inherits" clause would alleviate, but not solve, this problem.

You can build class inheritance by writing  expanded nested objects.

The catch is that each "class"  really wants a primary constructor method --- the one that returns the value of an object constructor to actually make the object ----  *with arguments* to set up the defs in the constructor.

Here's a thought: 

Then in a subclass we need a *different* primary constructor method with *more* arguments to set up any additional defs.
What you could do is in that subclass, you have to overried the superclass's constructor to call the subclass's own constrcutor, passing in a default argument (if you have one).   Note that if we had optional parts on messages, this problem could go away.

This file

-------------- next part --------------
A non-text attachment was scrubbed...
Name: classInheritance.grace
Type: application/octet-stream
Size: 931 bytes
Desc: not available
URL: <https://mailhost.cecs.pdx.edu/mailman/private/grace-core/attachments/20120809/f7a4d278/attachment.obj>
-------------- next part --------------


runs and prints 

> (4 at 4)
> (0 at 0)
> (1 at 1)
> (4 at 4 - black)
> (0 at 0 - black)
> (1 at 1 - black)
> (5 at 5 - red)

under 

> minigrace 0.0.7.1033
> git revision df1e167eb42df6333a8f927fea83de7107c6ddfe
> <http://ecs.vuw.ac.nz/~mwh/minigrace/>

(but doesn't seem to go on the web)

J



James




More information about the Grace-core mailing list