[Grace-core] Types in type definitions

Kim Bruce kim at cs.pomona.edu
Mon Jun 24 17:37:37 PDT 2013


If I have an object:

def x = object {
       method one -> Number {1}
       type Simple = {
              two -> Number
       }
}

Then what is the type of x?  Certainly it has type {one -> Number}, but is there a type that includes the nested type Simple?

I'm not sure what the right answer should be, and this likely opens up a whole ball of wax that I've been trying to avoid to this point.  

[At this point in the language development I'd like to have us say, for simplicity, that types may not be changed in inheritance, as that otherwise makes the type system quite complex.  We may want to add this later, but the type system needs so much work now that I would rather have us avoid those complexities until we get everything else working properly.]  

So what are the possible problems?

While minigrace does not currently support this (at least on the web), it seems reasonable to be able to declare type Simple as confidential (good for hiding implementation information from users -- as you might want to change it later).

Clearly only public information about types should escape, but how might it be used?  The following is one way:

var y: x.Simple := object {method two {2}}

While that looks weird in this context, I've been using it a lot with modules, which recall are represented as objects when they are imported.

Now let's look at a harder example:

type SimpliciT = {
    one -> Number
}

def x:SimpliciT = object {
       method one -> Number {1}
       type Simple = {
              two -> Number
       }
}

method m(x':SimpliciT) -> Number {
    def y:x'.Simple = object {method two {2}}
    y.two
}

print(m(x))
-------------------------

minigrace currently accepts this and returns 2.  I suspect that is because it currently isn't doing complete type checking, but shouldn't cause a problem at runtime anyway.

Now suppose I define 
(******)

def z:SimpliciT = object {
       method one -> Number {1}
       type Simple = {
              three -> Number
       }
}

and execute:
print(m(z))

Minigrace does indeed execute that and once again returns 2.  However, in a strongly typed world (whether static or dynamic), it should fail at this point because type z.Simple requires that there be a method "three", and there is no such method in the object associated with y.

Making it a bit worse, define a more complex value

def x = object {
       method one -> Number {1}
       type Simple = {
              two -> Number
       }
       def obj:Simple  is readable
            = object {method two {2}}
}

What is the type of x?  The public type of field obj depends on the type Simple defined inside x.  We might be able weasel our way around this because our type system is structural, and simply declare 

type SimpliciT = {
    one -> Number
    obj -> {two -> Number }
}

That requires the programmer to write the type a second time, but maybe that is OK.  It does generally require the programmer to write embedded types a second time if they escape their scope of definition.  However, another alternative that might be clearer would be to just allow type definitions in a nested way:

type SimpliciT = {
    one -> Number
    type Simple = {
              two -> Number
     }
     obj -> Simple
}

If we did allow type definitions to be nested, what would it mean to write
   z:SimpliciT

Would z need to be an object that has exactly the definition of Simple given above?  Would we allow the programmer not to bother to write Simple in the object, but just assume it is there (written exactly as in SimpliciT)?

I'm not sure what the right answer is to these questions, but here is the way that I'm leaning right now:

1. Allow types nested in other types -- i.e., include the definition of type Simple in SimpliciT above.

2.  If we are defining an object whose type has a nested type, then the programmer can omit the nested type as it will be treated as implicitly there.  Thus if we write 
     def other: SimpliciT = {method one {...}
                                             method obj {...} }

we don't have to write the definition of Simple again.  This doesn't add much complexity to the language and solves the problem illustrated in example (******).  If Simple is defined within the object, then its definition must exactly match the one in SimpliciT

Any thoughts?

Kim

P.S.  Both Java and C# allow nested types (interfaces in Java's case).  C# discourages the programmer from having public nested types, but we don't have much choice because of the way we handle modules as objects.


More information about the Grace-core mailing list