[Grace-core] Inheritance and object initialisation

Kim Bruce kim at cs.pomona.edu
Mon Jul 16 00:00:11 PDT 2012


Wow, this is a tough test for late on a sunday night ...
I'll send it now, but try to read it over again tomorrow.

Kim

On Jul 15, 2012, at 2:29 PM, Michael Homer wrote:

> ... I'm going to give
> some examples, and then generally questions on what particular
> expressions evaluate to afterwards to clarify.
> 
> First, the phrase that's been used is that the expression after
> "inherits" must "definitively statically resolve to an object
> literal". It's not quite what the spec says at the moment but it seems
> to have wide currency. I take from that that this should be valid
> (annotations omitted throughout):
>  def a = object {
>    def x = 1
>  }
>  def b = object {
>    inherits a
>    def y = 2
>  }
> That is required by the "inherits defaultObject" part of the recent
> notes as well, so I assume it works.

That should be fine.
> 
> Building on the above, I should also be able to write this:
>  def a = object {
>    def x = object { var t := 2 }
>  }
>  def b = object {
>    inherits a
>  }
> Does a.x == b.x?

Well, that get's down to what equality means.  In my opinion they should be distinct objects (because object is generative), but they are structurally similar.  Under the definition we've been talking about with egal, they should be different.
> 
>  def a = object {
>    def x = { self }
>  }
>  def b = object {
>    inherits a
>    def y = 2
>  }
> Does a.x == b.x? Does a.x.apply == b.x.apply?

Now it is getting interesting.  I see why you've been having issues with this.  With classes, the answer is clear, because nothing is generated until the object is created from the constructor.  That is, a class acts like a closure, where the initialization code is evaluated when the constructor as executed.  As you might be aware, Scala -- while it supports objects- only allows inheritance from classes.  I don't know why, but I suspect it may have to do with some of the issues you are bringing up.

While others have argued that classes should be seen as specialized objects, I've always looked at things the other way around.  Object expressions are evaluated by constructing the equivalent class (with parameterless constructor) and then immediately executing it.  This is a simple model (assuming you are happy with having classes as primitive -- and I am), and we can now understand inheritance of objects in these terms.

When obj2 inherits from obj1, the implicit class associated with obj2 just inherits from the implicit class associated with obj1.  The the parameterless constructor on obj2 is executed.  With your example above, that means that the inherited version of x is evaluated (again) when object b is created.

I would argue that this is the only reasonable interpretation.  Suppose we change the example slightly:

def a = object {
   def me = self 
}
def b = object {
   inherits a
   def y = 2
} 

I think it is pretty clear that a.me should refer to a, while b.me should refer to b.  Delaying the evaluation is the only way to accomplish that, and I would argue that any other interpretation would be counterintuitive.

> 
> How about this?
>  var tmp := 1
>  def a = object {
>    def x = tmp
>  }
>  tmp := 2
>  def b = object {
>    inherits a
>  }
> Does a.x == b.x? What is b.x?

Because of the delayed evaluation, b.x is 2. 
> 
>  var tmp := 1
>  def a = object {
>    def x = tmp * 2
>    tmp := tmp + 1
>  }
>  def b = object {
>    inherits a
>  }
> Does a.x == b.x? What is b.x? What is tmp?

a is evaluated before b, hence tmp has been increased before b is evaluated.  Because the body of a is re-evaluated when inherited, x is 4 and tmp ends up being 3.
> 
>  var tmp
>  def a = object {
>    def x = 1
>    method foo { "hello" }
>    tmp := self
>  }
>  def b = object {
>    inherits aCreator
>    method foo is override { "world" }
>  }
> Does tmp == a? Does tmp == b? What is tmp.foo?

I assume there is a typo here and aCreator should just be a.  As I indicated above, tmp should be b, so tmp.foo returns "world"
> 
> This should work:
>  method aCreator {
>    object {
>      def x = 1
>    }
>  }
>  def b = object {
>    inherits aCreator
>    def y = 2
>  }

Yes
> 
> More complicatedly:
>  var tmp
>  method aCreator {
>    object {
>      def x = 1
>      method foo { "hello" }
>      tmp := self
>    }
>  }
>  def b = object {
>    inherits aCreator
>    method foo is override { "world" }
>  }
> Does tmp == b? What is tmp.foo?

This is even clearer in that the evaluation of the super object is evaluated just before b is created, so tmp == b and tmp.foo is "world"
(though I must admit I might have missed something subtle here -- it's starting to make my head hurt!)
> 
> Or:
>  var tmp
>  method aCreator {
>    object {
>      def x = 1
>      method foo { "hello" }
>      tmp := { foo }
>    }
>  }
>  def b = object {
>    inherits aCreator
>  }
> What is tmp.apply?

"hello" -- is there any other possibility?
> 
>  var tmp := 0
>  method aCreator {
>    object {
>      def x = 1
>      tmp := tmp + 1
>    }
>  }
>  def a = aCreator
>  def b = object {
>    inherits a
>  }
> What is tmp?

tmp is 2.  The object isn't evaluated until a is defined, and then evaluated once more when b is defined.
> 
>  var tmp := 0
>  method aCreator {
>    def ret = object {
>      def x = 1
>      tmp := tmp + 1
>    }
>    ret
>  }
>  def b = object {
>    inherits aCreator
>  }
> What is tmp?

This is harder.  Is the original object evaluated when inherited (but not evaluated before then)?  I would say no using my reasoning from above.  The method aCreator is not evaluated except in the inherits statement.  Hence I would treat it as a class which has not been instantiated, hence I would give the final value of tmp as 1.  However, I must admit to feeling on shakier grounds with this one, as it is not quite clear what inheriting from a parameterless function means.  Clearly we need to evaluate it to get anything to make sense, but it is only evaluated to get something to inherit from.

I must admit my prime intuition here is from changing the example slightly to 

var tmp := 0
 def aClass {
   method new {
     object {
       def x = 1
       tmp := tmp + 1
     }
   }
 }
 def b = object {
   inherits aClass.new
 }

and then reinterpreting that as 

var tmp := 0
 class aClass.new {
     def x = 1
     tmp := tmp + 1
 }
 def b = object {
   inherits aClass.new
 }

There it is quite clear that after evaluating b, tmp = 1, as that is the first time any constructor has been evaluated.
> 
> What does a class do here?
>  var tmp
>  class aCreator.new {
>    object {
>      def x = 1
>      method foo is override { "hello" }
>      tmp := self
>    }
>  }
>  def b = object {
>    inherits aCreator.new
>    method foo is override { "world" }
>  }
> Does tmp == b? What is tmp.foo?

I think this example is wrong (or at least not what you intended) as there is an extra "object" label in aCreator that shouldn't be there.  Right now aCreator.new creates an empty object (it has no methods, variables, or defs), but has the side effect of evaluating the object expression and then dropping it on the floor.  At any rate aCreator.new is never evaluated on its own, only as part of the construction of b.  Moreover, an error would be obtained because of the "override" annotation on "foo" which doesn't override anything because a has no methods.

On the other hand if we erase the "object" from the first line of the definition of aCreator, then the definition of b is fine and tmp == b, and tmp.foo is "world"
> 
>  var tmp
>  class aCreator.new {
>    object {
>      def x = 1
>      method foo is override { "hello" }
>      tmp := { foo }
>    }
>  }
>  def b = object {
>    inherits aCreator.new
>    method foo is override { "world" }
>  }
> What is tmp.apply?

Again, I'm going to assume the "object" in "aCreator" is dropped as otherwise it is illegal.

Here again, tmp.apply should be "world", as tmp.apply should result in evaluating self.foo.  Because self is currently b, it should give "world".  This is actually a slightly different question from the others as we could treat closures somewhat differently if we wanted (though I believe this is more consistent).  I wonder what Scala does with something like this?  We should consider that when we decide what this should mean.  (I'm less firmly committed to this answer as I could see there might be arguments for treating these closures differently, but I believe that this is what most of us would intend by this)
> 
>  class aCreator.new {
> xxxx   object {
>      def x = 1
>      method foo is override { "hello" }  // omit override here!
>      def y = { foo }
>    }
>  }
>  def b = object {
>    inherits aCreator.new
>    method foo is override { "world" }
>  }
> What is b.y.apply?

"world"
> 
>  var tmp
>  class aCreator.new {
>    def x = { self }
>    tmp := x
>  }
>  def b = object {
>    inherits a
>    def y = 2
>  }
> Does tmp == b.x? Does tmp.apply == b.x.apply?

Yes and yes.
> 
> There are probably other cases I should have included too. My
> instincts for what many of these should be in isolation seem to be
> contradictory with each other in general, so I'd like to check how
> they're supposed to go.
> -Michael
> _______________________________________________
> Grace-core mailing list
> Grace-core at cecs.pdx.edu
> https://mailhost.cecs.pdx.edu/mailman/listinfo/grace-core



More information about the Grace-core mailing list