[Grace-core] Name resolution

Michael Homer mwh at ecs.vuw.ac.nz
Sun Mar 22 22:38:32 PDT 2015


This will address the name resolution rule for an unqualified
request, focusing on the dynamic semantics of finding the receiver.
It assumes that all eventual requests are dynamically dispatched on
an object found by the resolution rule, and/or an error of some sort
is reported.

First, a definition. The "scope chain" is an ordered list of
entities that can answer the question "do you contain a name X":
- Activation records, which say "yes" if they have a definition of X
- Object constructors, which say "yes" if they define X
- The module, which behaves as an object constructor
- The dialect, which behaves as an object constructor

with the end of the list being the innermost scope currently active
and each entry nested immediately inside the preceding entry.

Here are three possible renditions of the lookup rule for an
unqualified request "X".

In the system we will call "local":
- Look up the scope chain, without involving inheritance, until the
  name is found.
- If the name is found:
  - If it was found on an activation record, resolve to that.
  - If it was found on an object constructor, module, or dialect,
    dynamically dispatch on the relevant object suborned by that
    constructor.
  And terminate lookup.
- Look in the dialect, including inheritance
  - If found, dynamically dispatch on the dialect object.
- Resolve to a dynamic dispatch on "self".

In the system we will call "OC":
- The scope chain includes partial objects instead of object
  constructors. These partial objects also respond "yes" if an
  inherited method "X" is defined in a superclass.
The process is then:
- Look up the scope chain, including upwards inheritance, until the
  name is found.
- If the name is found:
  - If it was found on an activation record, resolve to that.
  - If it was found on a partial object, module, or dialect,
    dynamically dispatch on the relevant object of which that
    partial object is a part.
  And terminate lookup.
- Report a name resolution error.

In the system we will call "Q":
- The scope chain includes actual objects instead of object
  constructors. These objects respond "yes" if a request for "X"
  would succeed on the object.
The process is then:
- Look up the scope chain, including both upwards and downwards
  inheritance, until the name is found.
- If the name is found:
  - If it was found on an activation record, resolve to that.
  - If it was found on an object, module, or dialect, dynamically
    dispatch on the relevant object.
  And terminate lookup.
- Report a name resolution error.

All techniques at once, with their deviations marked:
- Look up the scope chain.
  - In "local", include only the literal bodies of object
    constructors in lexical scope at the request. Include the
    inheritance chain of the dialect only.
  - In "OC", include object constructors in lexical scope at the
    request and what they inherit.
  - In "Q", include the full objects lexically enclosing the
    request.
- If the name is found:
  - If it was found on an activation record, resolve to that.
  - Otherwise, dynamically dispatch on the object in which the name
    is known to exist.
  Terminate lookup.
- In "local" only:
  - Resolve to a dynamic dispatch on "self".


"local" is essentially the rule of Newspeak.
"OC" is essentially the rule of Java with regard to methods and
inner classes.
"Q" is essentially the rule of Ruby with regard to methods, classes
and modules.


Here are simple examples of different behaviours under different
systems. None of the following in any way involves the overriding of
types, family polymorphism, or method overloading.

    class a.new {
        method X {}
    }
    class b.new {
        inherits a.new
        def c = object {
            X
        }
    }
    b.new

In "local", this is a lookup error on "c" (final case). In "OC" and
"Q", this is a successful request on the instance of "b".

    class d.new {
        X
    }
    class e.new {
        inherits d.new
        method X {}
    }
    e.new
    d.new

In "local", this is a successful lookup on the instance of "e"
(final case) and a failed lookup on the instance of "d". In "OC",
this is a failed lookup for both classes. In "Q", this is a
successful lookup on the instance of "e" and a failed lookup for
"d".

    class f.new {
        def g = object {
            X
        }
    }
    class h.new {
        inherits f.new
        method X {}
    }
    h.new

In "local", this is a lookup error on "g" (final case). In "OC",
this is a lookup error. In "Q", this is a successful lookup on the
instance of "h".

    class i.new {
        method X {}
    }
    method X {}
    class j.new {
        inherits i.new
        X
    }

In "local", this is a request of the top-level method "X". In "OC"
and "Q", this is a request of "X" on the instance of "j". In both
"OC" and "Q" the module object masked by "self" as a receiver for
"X".

    def k = object {
        method Y {}
        class l.new {
            Y
            X
        }
    }
    def m = object {
        class o.new {
            inherits l.new
            method Y {}
            method X {}
        }
    }
    m.o.new

In "local", "Y" resolves to a request on "k" and "X" to a request on
the instance of "m.o". In "OC", "Y" resolves to a request on "k" and
"X" is a lookup error. In "Q", both "Y" and "X" resolve to requests
on the instance of "m.o". In both "OC" and "Q" "k" is masked by
"self" as a receiver for "Y".

"local" is essentially the rule of Newspeak.
"OC" is essentially the rule of Java with regard to methods and
inner classes.
"Q" is essentially the rule of Ruby with regard to methods, classes
and modules.
-Michael


More information about the Grace-core mailing list