[Grace-core] San Jose meeting

Andrew P. Black black at cs.pdx.edu
Mon Jun 6 19:20:54 PDT 2011


On 6 Jun 2011, at 17:49 , James Noble wrote:

>> 
>> a.  I don't believe we've talked about how to write down object types.  I propose the following notations:
>> 
>> interface T {
>>       m(a:A, b:B) : C
>>   x: T1
>>   writeonly y: T2   // see notes below
>>   readwrite z: T3
>> }
> 
> There is a bit in the latest spec. 
> 
>> I'd be happy to replace "interface" by "objecttype" (because I think of an interface as a module interface), but keep interface for now for consistency with Java.
> 
> Sure - and not just java. Tho if we don't have any other types this could just be "type"

I don't really object to interface, but "type" seems to be shorter and just what other people men by the word.  Did you have in mind reserving "type" for something else?

In the language, we use -> to indicate the return type, so it would make more sense to me to use it here too.
If we interpret : to mean typeof, the two possibilities that occur to me would be 

	m: A B -> C

and

	m(A, B) -> C

The names of the parameters are not part of the type. At least, if they are part of the type, they are surely ignored during type checking, so why have them?

> 
>> Here is how we interpret this:
>> 
>> -- Only methods are listed.  

And I'm fine with missing out the method keyword, since everything in a type is a method.

>> Thus x is a parameterless method, while m has the parameters shown.  The annotations "writeonly" and "readwrite" are syntactic sugar to make things look readable.  The first indicates that there is a method "y:=(y':T2)" while the second indicates there are a pair of methods z:T3 and z:=(z': T3).
>> 
>> I use these keywords because we don't want to commit to having y or z as actual instance variables (they could be overridden with methods)

Right, they might be instance variable accessors or ordinary methods.

>> and I think it is exceptionally ugly to have to write down method names like "y:=(...)".  Does this make sense and are you agreeable to this?  I am open to other notational suggestions.
> 
> I think it makes sense - not sure I at least agree. 

I think that it makes more sense to treat x and x:=() like any other methods — I don't see why they need a special syntax.  It's just one more thing for teachers to explain and for students to learn.  The students will have already learned that a var foo gives rise to a method foo:=(), so why make them learn something else?

> 
>> I'd actually prefer writing writeonly and readwrite after the declaration, and perhaps surrounded by "<...>" (either before or after the declaration), though I'm uncertain as to how to get it to look good, so I'm happy to hear other suggestions.
> 
> I think we should be able to get a common approach to metadata/attributes/annotations which might help here. 

I agree that in general, attributes are better going after the "thing" that they label.

> 
> But if the method is called x:= then shouldn't that be in the interface?  If we really dont like that syntax shouldn't we change it to match?  For what it's worth I think x:= isnt too bad and was convinced it was better then set_x

It's much better than set_x!  And it avoids the case-change problems of setX.  

We have to be a bit careful of reacting against something because it looks "ugly".  Things that are different from our conditioning _will_ look ugly — until we are used to them, after which they will look natural.   Students will be less conditioned.  For them, I think that uniformity is more important than being similar to some 20th century language.  So, I believe that we should either use x:=() everywhere, and learn to love it, or not use it at all.

>> b.  Modules
>> 
>> I would like to propose a simpler version of what I proposed in my talk at PLDesign.  Modules are of the following form:
> 
> Ok...  I wondered if you would. 

Cool!

> Implementation Module implements DMod, ... {
>    import D1, D2, ... (or better yet, D1.X, D2.Y, ...)


I like the late binding that Gilad gets by treating imports as parameters.  That is, you as the module implementor get to state your _requirements_, not how they must be satisfied.  So if your module needs a Set class, I as a client, get to choose which Set class, provided that it meets your spec.  If your module needs a constant maxNumberOfNodes, I, as your client, get to choose it.  Otherwise, we don't actually get modularity from our modules!

Moreover, providing modules with parameters also gives us the renaming that Eric Tanter was advocating.


>>    interface T { ... }
>>    class C implements T {x: S -> ...}     // implements clause is optional
>>    object o {...}
>>    const c:U := ...              (I still prefer "=" to ":=" to associate values to constants, but oh well ...)
> 
> So do we all but Michael was persuasive...

Aye.  It _is_ nice to be able to use = to mean equality.  What an idea!

> 
>>    var v:V := ...
>>    const f: T -> U := {x:T -> exp}   // is this how we specify closures?  

It's how we have been specifying then up until now.   Obviously, we could require a keyword function or fun or fn or λ.  The benefit would be making it clear to the novice.  The cost would be making closures bigger.  
For me, the advantage of not having to write

	if condition then function {oneBranch} else function {otherBranch} 

is clear, and brevity wins hands down.

In your example, the type spec would be inferred, unless exp was not a U.

>>    const makeT(x:S): T = C.new(x)   // a "factory" method

I'm a bit confused.  If it's a _method_, then wouldn't I write

	method makeT(x:S) -> T { C.new(x) }

If it's a _function_, then I would write

	const makeT := { x -> C.new(x) } 

The difference would be in the way that they are used (makeT(foo) vs makeT.apply(foo)), and whether we allow top-level methods in modules.

>> }
>> 
>> [Obviously we would allow more than one of each of these kinds of definitions]
>> 
>> Definition Module DMod {

The word definition has the wrong connotation to me — it says that I'm providing all of the details.  You mean the opposite, I think — that this is just the interface of the module.  Wouldn''t this be a better place to use the word "interface", or maybe "api", if one were into TLAs

>>     interface T {...}  // If this is the same as that inside implementation, then only list it here, not in implementation
>>          // Can also hide some methods, giving a partial revelation:
>>     interface T extends {
>>           m:....
>>           ...   // list only features you want publicly available outside of module
>>     }

Are you defining T twice, with different meanings, or is one of those Ts a U?

>>    const o: U    // list type, but not implementation
>>    var v: V
>>    const f: T -> U
>>    const makeT(x:S): T
>> }
>> 
>> So basically, identifiers (and their associated types) are listed here, but implementation information is left out.  We could list classes, but I would discourage that compared to factory methods like makeT.

Oh, I see  your point, I think: you don't want to say that a particular factory method is implemented in a particular way.  But that's already a feature of our class notation.  I think that the right thing is to export a factory object, with as many methods (called new, or makeT, or whatever you want) as is appropriate.  That says nothing about they way that object is implemented.

>> 
>> As shown in the example, we can provide partial information about an interface.  Leaving out method definitions in partial revelations would allow the designer to hide those methods.  (As I stated in my talk, I believe we could decide to entirely leave out access annotations like public and private from classes and just use the module system.)

I don't see how this would work in a dynamic language.  If I have an object o, either it understands the method x or it doesn't.  Even if it statically has type U, where U excludes x, requesting the method x will still work.  There is no way of saying that an object understands x if requested from module A but not from module B.  We could provide such (using stack inspection, or something similar), but the result is not OO, because it doesn't operate on a per-object basis.

Our paper "Object-oriented encapsulation for dynamically typed languages"  (OOPSLA 2004) http://web.cecs.pdx.edu/~black/publications/EncapsulationOOPSLAProceeedings.pdf  does provide a way of doing OO encapsulation.  Essentially, every reference has access rights; it's a bit like a capability system, really. 


>> 
>> Feedback is welcome.  If any of this is obscure, let me know and I will elaborate.
> 



More information about the Grace-core mailing list