[Grace-core] Class syntax and type parameters

Kim Bruce kim at cs.pomona.edu
Thu Nov 20 00:56:48 PST 2014


I'm afraid that the volume of e-mails has made it difficult for me to keep track of what the actual problem is and how the proposals are solutions to whatever the problem is.  As I've stated often, I don't have a problem shifting to [] instead of <> for generics, but I don't see the problems that are pushing us for more exotic use of type parameters -- and I especially don't understand why people don't want methods with type parameters.  Some people seem to feel that add a lot of complexity.  I personally don't see that as a problem compared with many of the other things being slung around -- and I don't see a way of avoiding them with common circumstances like map operators on data structures (of all sorts) and using something like the visitor pattern.

I had hoped someone would write a succinct position paper explaining what the problem was and how the proposals seem to solve the problem.  However, unless it blew by when I wasn't looking I don't think anyone has presented that.  Repeats of pieces of old e-mails just give me disconnected fragments in a conversation whose context i don't understand.

At one point it appeared that the problem was interpreting type parameters on classes, esp. those with dotted names.  Let me attempt that and then you can tell me why I'm missing the point -- because I think I must be.

We have a simple translation for undotted classes:

class c[T](a,b) {
   method m
   method n
}

gets translated as

method c[T](a,b) {
   object{
      method m
      method n
   }
}

However it seemed harder to do this with dotted classes unless we move the type parameter to the right of the ".":

class d.new[T](a) {
     method m
     method n
}

translates to

def d = object {
   method new[T](a) {
      object {
          method m
          method n
      }
   }
}

but that translation doesn't work if we want class d[T].new(a).  For that we can use the following translation (was this Andrew's suggestion? -- I've lost track):

class d[T].new(a) {
     method m
     method n
}

translates to

method d[T] {
   object {
      method new(a) {
         object {
             method m
             method n
         }
      }
   }
}

If we wanted a more uniform translation for generic and non-generic we could just change the "def d = object {"
in the first translation to a parameterless method:  "method d {object {"

That seems pretty straightforward to me.  It does of course use generic methods.

I must admit to being unsure as to how the statically typed language is going to blend into a dynamically typed language.  I have assumed that a missing type parameter will be syntactically transformed into an instantiation with the type Unknown.  I don't know if that is enough to make everything work smoothly or if we need something more exotic -- I hope not, but I haven't had a chance to explore it and I don't know if any of you has explored it either.  If you have, I would hope you could write up what you've learned to help the rest of us.  Meanwhile, I'm fearful enough about the complexity that I don't want to make the language even more complex.

So given all this, please tell me what the real problem is that you are trying to solve, and why what we have now does not solve it.  Until I have a clear statement of that I can't give a half decent evaluation of the vague proposals that have been in the various e-mail messages.  Until that point all I can do is to continue to express my puzzlement as to why we seem to want to make a language designed for novices more complicated than the current design (and I do understand that minigrace may allow one to do things well beyond what we had originally planned, but I don't see that as a reason to build these features into the language.

Bottom line is I can't really respond coherently to these proposals without a clear statement of the problem, the proposed solution, and why the solution really solves the problem.

Meanwhile I think we have a very nice language for novices and would hate to see us mess it up by walking into a tar pit.  If we have problems, we need to fix them, but in many cases I'd rather give up power/expressiveness rather than make the language more complex.

Kim

P.S.  I don't think I'll have a chance to write a detailed response to Tim and Michael's proposal on what can be inherited from.  While we definitely need to nail this down, we should work hard to keep this simple.  While inheritance can be useful, most people now agree that it was oversold at the beginning and should be used rather sparingly.  If we have to code around limitations, then so be it.  In particular, I certainly wouldn't worry about having the language support family polymorphism of any flavor at this point.  Almost all of the time we will be inheriting from a class that is directly in scope (or inherited directly) or the clone of an object that we know concretely.  Tim and Michael's proposed definition seems to restrict to the right kind of things in that way, but I'm not sure I'd worry about even going that far. If we can take care of 90-95% of the use cases we see for novices then we should be fine.  Admittedly sometimes library writers won't be able to do everything they want, but if they can code around it I don't see it as a big deal.  We know inheritance is complex -- let's try not to make understanding when we can use it even harder.  Simple and clear should always be our mantra.


On Nov 18, 2014, at 10:51 PM, James Noble <kjx at ecs.vuw.ac.nz> wrote:

> Hi all 
> 
> a summary & some more comments on the "square bracket" proposal:
> - brief intro
> - some precedents next, cos Kim asked for three know uses :-)
> - the details below, just copied out of a more restricted discussion to grace-core
> 
> 
> 
> Intro:
> ===
> 
> The idea is - for both generic types & classes - we'd write:
>   Foo - for a nongeneric type, or a generic instiantiated all at Unknown
>   Foo[Bar] - for an instantiated generic type 
> likewise
>  foo.new - to instantiate a nongeneric class, or a generic instiantiated all at Unknown (the name "new" is not special)
>  foo[Bar].new - to instantiated a generic class at Bar
> 
> in both cases, the operational semantics are that Foo/foo denote an object, and [] is the "indexing request"
> 
> This would mean most students would *not* have to write generic _requests_ or _methods_  (as Marco has suggested)
> This would require that we kept the [] indexing request in the language
> 
> 
> Precedents:  ("three known uses") 
> =======
> 
> * Omega, a statically-typed language based on Self by Gunther Blaschek used just this design
>     although restricted to a single parameter:
>      Array -- array of anything
>      Array{Integer} -- array of integers
>     (see http://www.ssw.uni-linz.ac.at/Teaching/Lectures/POPL/OOPwPT.pdf, in Background Papers)
> 
> * Beta and gbeta use generic instantiation is inheritance;
>     at this level the designs work in the same way, modulo syntax.  
>      SortedList -- a list you can put anything in 
>      SortedList(# T::Integer #); -- a list of integers 
> 
> * Dylan  "<array>"  -- is an array that can hold anything, while
>        "limited(<array> of: <single-float>)" -- can only hold single flotas
> 
> 
> Actual Details:
> =========
> 
> Square Brackets for Types
> ================
> 
> From: Michael Homer <Michael.Homer at ecs.vuw.ac.nz>
> Subject: [Grace-core] [] for types' type parameters (with possible application to classes)
> Date: 14 November 2014 12:31:11 pm NZDT
> 
> Instead, we could have (reified) types consistently be objects,
> generally bound to names. Providing them with generic parameters would
> use an ordinary method call. A plausible method to use is the existing
> postcircumfix [] operator from the specification.
> 
> In this case we would fully abandon the use of <> for generic type
> parameters on types. <> would be reserved for declaring and requesting
> methods only. Types would always use []. Static typing would be
> exactly the same as it has been, with just the syntax different.
> 
> Given a type currently written:
>  type List<T> = type { first -> T ... }
> there is be an object in existence representing the type. That object
> is bound to the name "List". It is, itself, a pattern, matching
> List<Unknown> (that is, all lists).
> 
> In this design the object would also have a method [], which would
> specialise the type according to the provided parameters. It would
> return an object representing the newly-instantiated type. The
> implementation of [] would be internal, and memoise the results. So:
>  def ListOfStrings = List[String]
> will create an alias for the instantiated type, while
>  def MyList = List
> will alias the uninstantiated type - these are always genuine object
> references. In both cases, the same object will always be returned.
> Importantly, the "MyList" alias can be specialised in exactly the same
> way, because it is a real object alias.
> 
> All occurrences of parameterised types would use the same syntax:
>  var x : List[String] := ...
>  method lengths<T <: Measurable>(l : List[T]) -> List[Number] { ... }
>  type Listable[T] = type { asList -> List[String] }
> But to request the "lengths" method, you would still use <>:
>  lengths<String>(x)
> In this way types and methods are clearly separated, and types are
> always first-class.
> 
> This also raises some possibilities of higher-kinded types, because
> objects can be passed around and have methods requested of them in the
> ordinary way. That might be useful in some cases. It's unclear whether
> it should be possible to re-specialise the type:
>  def ListOfStringNumbers = ListOfString[Number]
> If it is possible to do, the types should probably be &ed together.
> 
> It is possible that this same approach could be applied to classes, if
> instantiating generic parameters on the class itself is considered
> useful. The class would be an object, able to be specialised by
> requesting its [] method, or instantiated with its constructor method.
> This complicates the encoding, but retains the class as a first-class
> object.
> 
> 
> 
> 
> Square Brackets for Classes
> =================
> 
> (this expands on the last para of MIchael's email above)
> 
> From: James Noble <kjx at ecs.vuw.ac.nz>
> Subject: Re: [Grace-core] Class syntax and type parameters
> Date: 14 November 2014 9:37:29 pm NZDT
> 
> the code is below: basically one writes
> 
> box.new(i)                      to get a Box (aka Box[Unknown] initialised to i
> box[Number].new(23)     to get a Box[Number}
> 
> so this syntax lines up quite well with the postcircumfix type syntax.
> 
> This does provide a reason for keeping dotted classes
> (Michael's arguments this morning at least half reconvinced me to
> head back to undotted classes-as-methods)
> and the "class macro syntax" would get a bit odder...
> 
> class box[T].new(init} {
>  var value : T := init 
>  method get -> T {value}
>  method set(newValue : T) -> Done {value:=newValue}
> } 
> 
> could expand to something like:
> 
> def box = object {
>  method new(init) typed(T) {
>      object {
>          var value : T := init 
>          method get -> T {value}
>          method set(newValue : T) -> Done {value:=newValue}
>      }
>  }
> 
>  method new(i) {new(i) typed(Unknown)} 
>  method [](t) {
>        object {
>            method new(i) {outer.new(i) typed(t)}
>        }
>  }
> }
> 
> def b1 = box.new("hello") //new Unknown box
> 
> def b2 = box[Number].new(23) //new box of Numbers
> _______________________________________________
> 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