[Grace-core] Inheritance and Template Objects

James Noble kjx at ecs.vuw.ac.nz
Wed Apr 3 18:28:41 PDT 2013


Hi Andrew

I'm very very tempted to say - see we all agree! - and are just fighting about secondary things.
A few quick questions here, more comments later..

James

> I think that my concerns about inheritance may be somewhat different from those of Kim and Michael; I'm less concerned with similarity to Java and with ease of implementation than with an inheritance system that is easy to describe, and yet still lets us do the wind variety of things that we have discussed.  These include
> 
> 	(1) composing initialization,
> 	(2) inheriting initialization code,
> 	(3) not generating new objects unnecessarily, only to drop them on the floor moments later,
> 	(4) Allowing for extension to something like MI or trait composition, at a later time and in higher language levels,
> 	(5) Allowing inheritance to work between class objects,

I think we all want this, well perhaps other than (5).   And I think we can get it all. 
, 
> I'm going to propose that Grace include a new kind of object, which I'm going to call here a template object. (Yes, sure, you can call it a trait if you like.)

or a class?
> 
> Template objects are real objects.   Like types, they are meta objects, that is, they are used to describe, indeed to prescribe, the application domain objects.   There are two "odd" things about templates:
> 
> 	(1) They are denoted by a special language syntax, which looks like this:
> 
> 	     template  { 	def c_i = epr
> 			  	var v_i 
> 			  	method m_i { expr }
> 			  	e_i				// initialization code
> 			     }
> 

is the order fixed here, or is it free? 


> 	    This is exactly the current object constructor, except that the word object is replaced by template.   (It's likely that the 
> 	    keyword template can often be omitted, since it can often be inferred from the context, just like we can
> 	    often omit the keyword type.

OK so free then...
> 
> 	(2) Templates are nominally typed.  So the only things of type Template are created by the above syntax, or by operations on 
> 	     templates (to be discussed, but including a single inheritance operation and perhaps the trait composition operations).

not sure here. I'll accept it for now, but if they really were nominal, I think it breaks your interval example as the two templates would have different nominal types.  But I think I know what you mean - perhaps something like singleton types?? - which is fine.

> In these two respects, templates are just like types.   It may be that templates, like types, also need to be parameterized.
> 
> In this proposed new world, objects are created, as they are now, by executing some code that looks like object { ... }, only now we call this "instantiating an object template" rather than "executing an object constructor".  (Longhand, that's now object template { }, but we can omit the template keyword).  

would programs normally use "object" to make new objects (like Java's "new")
or would they call methods. This "object" isn't a method request, is it?

> So, where's the beef?
> 
> The innovation is that an object carries its template around with it for the whole of its lifetime — just like a Java or Smalltalk object carries its class around with it.  After an object has been created, we can ask it for its template, just as we can ask it for its type, thus: 
> 
> 		myObject.template			or templateOf(myObject)
> 
> I'm not sure which is the better syntax; it should parallel that for getting a run-time type.

right.  to deal with vampires, myObject.template or mirrors.templateOf(Object)

do you think of this as as base-level operation or a metal-level operation -
i.e. when would people have to learn about templates (and "object") 

> Here's the key idea:  we can then use this template to create more objects, or as the basis for inheritance, or for trait composition.  For example
> 

sure. I'll try to rewrite these examples later...
have to go now

> 		class aMatch.successful(b, m) {
> 				inherits templateOf(true)
> 
> 				def bindings is public = b
> 				def matched is public = m
> 		}
> 
> 		def emptyness = template {
> 				method size is requirement {}
> 				method isEmpty { self.size = 0 }
> 				method notEmpty { self.isEmpty.not }
> 				method ifEmpty(block) { self.isEmpty.ifTrue(block) } 
> 				method ifNotEmpty(block) { self.isEmpty.ifFalse{block.apply(self)}} 
> 				method ifEmpty(block)ifNotEmpty { self.isEmpty.ifTrue(block)ifFalse{block.apply(self)}} 
> 		}
> 
> 		class aSet.with(args*) {
> 				def rep = aList.empty
> 				uses emptiness + enumerable 
> 				method size { rep.size }
> 		}
> 
> 
> Because the template contains the initialization code, the inheritance and using operations can combine the initialization code from several different templates and execute it on a newly created object.
> 
> I see two issues:
> 
> 	(1) 	The static typists will want to place restrictions on what templates can be used to create objects.  These will be similar to 
> 		those restrictions that we have placed on the inherits expression: the intent is that the compiler can figure out the 
> 		value of a template statically.  This means applying templateOf to a statically known value, or using a literal template,
> 		or a combination (using trait +, inheritance) of other statically known templates, perhaps with method exclusion,
> 		aliasing, etc.

sure  the "noinal" types do the work here

> 	(2)	In general, templates will need to be parameterizable.  For example:
> 
> 		def aCartesianPoint = object {
> 			method x(a)y(b) { 
> 				template {
> 					def x is public = a
> 					def y is public = b 
> 					print "nothing will be executed until an object is made from this template"
> 				}
> 			}  
> 
> 		class colorPoint.x(x)y(y)color(c) {
> 				inherits aCartesianPoint.x(x)y(y)
> 				def color = c
> 			}
> 
> 		This means that templates will often be returned from methods, because methods are the only things that take parameters.
> 
> (1) and (2) may conflict.  Mostly, Michael's "tail call" rule will work, I think, but this will need to be hammered out.
> 
> How is this different from what we have?  In practice, maybe not at all.  Conceptually, every objects now contains a template that describes not only its methods but also its defs and vars and its initialization code.  It has always had most of that stuff anyway, because the implementation wants to share it, burt we have never talked about it at the level of the language.  Now, I'm proposing that we do.
> 
> I think that we actually need the template keyword.    Consider what happens if we change def aCartesianPoint above to be
> 
> 		def aCartesianPoint' = object {
> 			method x(a)y(b) { 
> 				object {
> 					def x is public = a
> 					def y is public = b 
> 					print "this will be printed whenever x()y() is requested"
> 				}
> 			}  
> 
> The difference is that aCartesianPoint.x(3)y(4) now answers a Point object rather than a template for a Point object, and consequently requesting x()y() on aCartesianPoint now prints the message 
> 
> 
> 		class colorPoint.x(x)y(y)color(c) {
> 				inherits templateOf(aCartesianPoint.x(x)y(y))
> 				def color = c
> 			}
> 
> We can still inherit from the result of x()y(), but we now have to request its template; the resulting colorPoint objects will look the same both ways, but the side effects will be different.  That is, without the template, creating a colorPoint will have the side effect of creating aCarteianPoint, printing  "this will be printed whenever x()y() is requested", taking its template, and then dropping the CartesianPoint on the floor.  This is the behavior that we want to eliminate.


so is the message printed in inheritance?  
if so when / how
i.e. when does the template initialisation run..


> Explicit templates avoid James's "vampires": if James give me an encapsulated object, I can grab its template, but that does not help me to grab its data.

well we control templates the way we control mirrors...

> Because templates can be returned from methods, we can choose templates dynamically.  For example, suppose that I have code
> 
> 	def upInterval = object anInterval.from 0 to 10 by 2
> 	def downInterval = object anInterval.from 10 to 0 by (-2)
> 
> 	def anInterval = object {
> 			method from(f)to(l)by(s) {
> 				template { 
> 					uses enumerable
> 					def first = f
> 					def last = l	
> 					def step = s
> 					method do (block) { if (s > 0) then ...  }
> 				}
> 			}
> 	}
> 
> I might argue that this is bad style, and that anInterval should be a class, not a template, but there you are ... 
> 
> Suppose that I decide to split the implementation of upward and downward intervals.  I can do this without forcing the client to refactor:
> 
> 
> 	def anInterval = object {
> 			method from(f)to(l)by(s) {
> 				if s > 0 then {
> 					template {
> 						uses enumerable
> 						def first = f
> 						def last = l	
> 						def step = s
> 						method do (block) { ... // code for upward interval }
> 					} 
> 				} else {
> 					template {
> 						uses enumerable
> 						def first = f
> 						def last = l	
> 						def step = s
> 						method do (block) { ... // code for upward interval }
> 					}
> 			}
> 						
> 
> Now the template that I get back from from()to()by(s) depends on the value of s, but since they both have the same template type, everyone should be happy.
> 

more later

j

> 
> 
> 
> 	 
> _______________________________________________
> Grace-core mailing list
> Grace-core at cecs.pdx.edu
> https://mailhost.cecs.pdx.edu/mailman/listinfo/grace-core
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailhost.cecs.pdx.edu/pipermail/grace-core/attachments/20130404/28b00a49/attachment-0001.html>


More information about the Grace-core mailing list