[Grace-core] initialisation & checking

Kim Bruce kim at cs.pomona.edu
Sun Apr 28 15:20:02 PDT 2013


First, do I remember correctly that we are not meeting this week (Monday or Tuesday)?

I must admit, I don't see that this is a huge problem.  Other languages do it.  Sometimes the rules are more complicated, but it is doable.

I would hate to see us ban executable code that might use "self" in intiitialization.  The REPL case makes it clear we want to be able to execute code before the object is complete, including calling newly defined methods (which are, after all, self calls).  The complicating feature there might arise if we allow a REPL to start with an "inherits" clause, as we might then execute a self call early to a method that we redefine later, thus giving the wrong syntax.  That could be overcome by either not allowing "inherits" clauses at the top-level of a REPL or not allowing redefinition if we do allow "inherits" clauses in a REPL.  We also would not allow reference to methods/variables/definition that have not yet been defined/declared in the REPL.  Those restrictions seem pretty straightforward, so, let's not worry about that for the moment and instead look at the general case.

Let me make up some new terminology to help describe initialization.  Let's take the statement that we "prepare" an object to mean that slots are allocated for all definitions and variable declarations of an object and method definitions are loaded into slots for methods.  However, this does not include elaborating definitions or initializing variables.  Thus after "preparation", an object has been structurally formed so code accessing definitions, variable declarations, and method invocations  can be set up properly (even if that code would crash if actually run at this point).

How do we prepare an object with an "inherits" clause?  While I don't believe we have specified this before, it is certainly easy to require the "inherits" clause to be the first thing that appears in a class definition (if it occurs at all) -- before new definitions, variable declarations, or methods.  Then to prepare an object being formed using an inherits clause, we would go through the process of preparing the object/class being inherited from, while also inserting the slots for the new definitions and variable declarations, and doing the insertion of new methods and overriding old ones.  In particular, if a method is inherited and overridden, only the new overriding method body is available.  (I'm ignoring "super" calls fin this e-mail.)  Note that at this point, no definitions have been elaborated or variables initialized, whether from the inherited or new object..  We could then go through and run the initialization code of the superclass/object (i.e., elaborate definitions and initialize variables) where "self" is interpreted as the new object, followed by running the initialization code of the new object being defined.  

[While I haven't indicated it here, I think one could pretty easily specify the order of the initialization code for each object -- basically from top to bottom.  Methods have the advantage that nothing is executed when they are defined and installed -- they are closures, after all, so the order of installing them makes little difference as long as the overriding methods replace those being inherited.]

Of course, when running the initialization code for the superclass, a method overridden by the subclass may be invoked and that may access a definition or variable that has not yet been initialized.  That is a programmer error and would cause a run-time error.  We could work hard to make those impossible to write with legal code, but I personally think that is overkill.  I believe students need to understand something about the order of initialization.

I suspect there are some corner cases I haven't considered above, and we can argue about whether we want initialization in variable declarations to be treated differently from initialization in assignment statements that are not part of declarations (though, having just talked about this in C++, I would prefer not to get into similarly horrible distinctions in Grace).

So, I'm sure you'll let me know where I've ignored important cases, but in the worst case we can simply look back and do what Java or Scala do.  It works, and seems not to confuse programmers.  Is there anything we are doing that would make it worse than in those cases?

Kim



On Apr 27, 2013, at 10:26 PM, James Noble <kjx at ecs.vuw.ac.nz> wrote:

>> Yes, I agree that this is a serious problem.  The root cause is that we have been using "top level code" in object constructors to do two things.
>> 
>> (1) In the REPL, and in Modules, it's just code that should  be executed asap.  In the REPL in particular, we want it executed before the object is completely created, which ca't happen until the END of the object constructor is reached.
> 
> in some cases at least, this is the case for code in objects generally.
> Certainly for debugging, its great to just dump some code in there to print or something.
> 
>> (2) In inherited objects, we want to delay the execution of the top level code until after all of the sub-objects have been created, since the sub-objects might change the meaning of any of that code.
> 
> right. I now think this *is* a special case, we don't support it, and we probably should.
> But it's really only required for framework builders, not even simple inheritance
> for modelling (I think).
> 
>> There desires are fundamentally in conflict.   We can eliminate that conflict by banning the offending code.  Other proposals are welcome!
> 
> banning the offending code isn't enough.
> Some way of doing the right thing would be nice!
> 
>> If we do ban executable code in object constructors, then we would need to make modules and "scripts" something else.    The obvious choice is methods, but I have to admit that I really dislike the idea of a "main method" as in Java — the name "main" makes it sounds as though that method should be important, whereas in fact it's unimportant.
> 
> right. so let's not do that...
> 
>> Michael is also right that our model for doing procedures-first required the incremental growth of objects by adding methods, which could be requested as soon as they were added, even though the object is incomplete.  It does seem a shame to loose that — but I am more concerned with having a sensible semantics for objects than being able to use methods like procedures.
> 
> whether the REPL is a module or a method or whatever, it's going to be special:
> it has to run when it is in some sense "incomplete".
> But I think it always makes sense to build the "structure" of the whole object,
> including method, before doing initialisation so I don't see this as a problem. 
> Forbidding inline code is a problem. 
> 
> Again - surprisingly perhaps - I'd like clear, straightforward explicable semantics,
> first of all - something to tell the children as Andrew puts it.  If we want to rule 
> out programs, we can do that with a dialect. I don't see that a dynamic variable not
> initialised error as worse than a dynamic no such request error - a type system could
> rule that out, but that shouldn't be part of the fabric of the language.
> 
> James
> 
> 
> _______________________________________________
> 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