[Grace-core] More thoughts on modules

Andrew P. Black black at cs.pdx.edu
Thu Jun 9 13:03:17 PDT 2011


On 8 Jun 2011, at 10:43 , Kim Bruce wrote:

> The intention is that the definition modules would be early bound (compile time), but the implementation modules would be late bound (e.g., at link time).  I see modules as static, so don't see the need to bind at run time, but am happy to listen to arguments in favor of that.
> 

I think that the argument in favor of run-time binding  simplicity of semantics.  "Compile Time" and "link time" aren't (at present) semantic concepts in Grace.  If we invent these concepts, and a notation for them, then we have to give that notation semantics.

I would rather have a single semantics of parameterization and initialization, and let the fact that they can be done at link time be an optimization (which we enable through careful design), rather than something that requires a whole new category of semantics.

This is the approach that we have said that we are going to try to follow with types — that they should be assertions carefully designed to be compile-time checkable, but whose semantics says that they guarantee runtime properties.  I think that our discussion in Milpitas says that this is the only way in which we can make sense of typing in a mixed static & dynamic environment.

>> Implementation Module Andrew implements DMod { EMod: EInterface ->
>> 
>>  type T = EMod.OT
>> 
>>  type U = EMod.OU
>> 
>>  const o = EMod.oo
> 
> This appears to me to be pretty much a notational variant of what I wrote.  How (and when) would you instantiate it?  Unless something pops up that is surprising, I have no strong preferences as to which notation is better.

Well yes, it is a notional variant.  It uses the block notation to say where the parameter, which is an implementation module, comes from.  I would parameterize it like this:

const AndrewsParamerterizedModule := Andrew(EModImplementation)

You presumably have some other parametrization mechanism in mind that enables this to be written at "link time".  Is this a new DSL for linking, or a subset of Grace?   If the latter, I imagine that it would look rather like what I wrote above.

>> I think that's more or less right.  But note that encapsulation from Modula-3 modules is very much more limited than what we describe in our paper, which is object encapsulation.  It lets you do things like have full access to a collection from one objector (its "owner", say), but read-only access from other objects.   Neither of these clients is the implementor of the collection.
> 
> I think this is where the multiple interfaces for an implementation module in my presentation kick in.  Depending on what privileges you want for that object, you would import and/or use one or the other interface module for the same implementation.  I can't tell if your scenario provides more flexibility than the one I outlined.

Can you explain how you would do this in your proposal?   My understanding of Modula modules is that there are exactly two views of a data structure, one inside the implementing module, and one outside.

In the OOPSLA paper proposal, the owner of the collection (a Morph, say) has full client access to it.  This means tahtit can add and remove members, but it can't look at the internal hash table.  One of the methods in the collection is as follows (re-writing in Grace syntax)

	method asEnumeration {selfWithPolicy enumeration}

This answers a reference to self (the collection object) with a more restrictive encapsulation policy, specifically, one that allows enumeration but not change.   Now the morph can expose a method to its clients

	method subMorphs {collection.asEnumeration}

This method will answer a reference to the collection with the restricted policy.

Section 5 of the OOPSLA paper explains the various design choices and why we thought (in 2004) that our choice was correct.  Reading it again now, I still believe the reasoning.  However, I also have the reaction: encapsulation is far too complicated for a teaching language.   We may still want it, to protect libraries and "teachpacks" from students, but I can't imagine teaching it in the first couple of courses.

(Note that "selfWithPolicy" would be a new language element that lets one amplify, or restrict, the encapsulation policy applied to self.  It took me a minute to remember why this isn't just a method request self.withPolicy.  The answer is that a withPolicy message, if generally available, could restrict an encapsulation policy but not amplify it, or else anyone could break the encapsulation.  However, selfWithPolicy is safe, because you already have full access rights to yourself.)

>> A while back, when you proposed modules, I asked you what problem you were trying to solve with them.  I tink that modules can solve namespace problems, and provide a mechanism for deployment.  They don't really address encapsulation problems in a very useful way; for that one needs something like encapsulation policies.
> 
> I'm not sure they don't provide something close to what you outline, though probably in a slightly less expressive/flexible way.

I can see how Kim's module proposal solves some (object) namespace and deployment problems.  (methods still have a common namespace, I believe.)  I don't see how it addresses encapsulation.

What do I mean by this?  I think that an object-based encapsulation mechanism has to explain two things:

	(1) Validity: which method requests can be made of an object.   This is so that one line of code can ask "what's in your hashTable?", but another sender can't.

	(2) Lookup: given that a message is valid, i.e., can legally be requested, which implementation is executed?  This is what's necessary to provide something like private methods. When an objects implements a private method p, self.p should always request execution of that method, even if a subclass implements a new public method also called p. 

The mechanism in the OOPSLA paper has rules for both of these things (in section 4.4).  I don't know what your rules are.   If you can explain how they achieve these things, then we can compare the mechanisms.

An intriguing alternative to private is to allow lexically bound procedures as well as dynamically bound methods.  This is what would be necessary if we wanted to allow control structures to be defined in a (fictional) surrounding scope, which is just what most languages do for the "standard library").  Then the encapsulation mechanism would not have to worry about private at all, which is, I think, a great simplification.   I'm a bit concerned that it will be confusing to have both procedures and methods: foo(3) would be a procedure call, and o.foo(3) would be a method request.   foo(3) would always be lexically bound, and thus private.  

	Andrew





More information about the Grace-core mailing list