t, u ::= term
| x variable
| t . l selection
| t / { cm } refinement
| new T instantiation
TT ::= type
| TT # L selection
| TT / { cm } refinement
| { x J : TT K ⇒ m
i
i
} structural type
| TT
1
& TT
2
intersection type
| p . type singleton type
| Any top type
| Nothing bottom type
| TT necessarily TT
T , S ::= type or un-type
| TT type
| Un [ TT ] un-type
m ::= member
| val l : T J = t K value member
| type L : K J = T K type member
cm ::= refinement member
| val l = t value member
| type L = T type member
Figure 1. Scalina Syntax (terms and types)
must be made for abstract members. We introduce un-members,
which safely model the input to an abstraction, and re-use tradi-
tional members to represent the result of the abstraction. Thus, an
object with un-members may be thought of as a curried function
that takes its keyword arguments in any order. The members of
such an object represent its results.
We study purely object-oriented abstraction in a dependently
typed, three-level calculus that uses the same concepts for abstrac-
tion and computation on terms and types. As in the νObj calculus,
function application is decomposed into refinement and member
selection. Because the level of types is modelled after the level of
terms, a type-level function is modelled as a type with type un-
members.
The distinction between un-members, which behave contravari-
antly, and normal, covariant, members, is instrumental in proving
soundness on the level of types and kinds. Due to the symmetric
design of our calculus, the soundness proofs proceed by similar ar-
guments at both levels.
2. Scalina: Syntax and Intuitions
Scalina is a three-level object-oriented calculus: we distinguish
terms (objects), types, and kinds. Terms are for computation, types
are used for classification as well as computation, and the role of
kinds is strictly limited to classification. Computation is performed
using two mechanisms: member selection and member refinement.
Classification is more intricate, ranging from merely structural de-
scriptions of the classified entities over nominal classification, the
intersection of classifiers, singletons, and strictly empty classifiers.
2.1 Syntax
Figures 1 and 2 outline Scalina’s syntax. We use ‘J .. . K’ to denote
the optionality of ‘. . . ’.
The term level consists of member selection, member refine-
ment, and instantiation. Analogously, a type may be a type selec-
tion, a refinement or a structural type. A structural type binds the
self variable x in the members it includes; if the type of the self
variable is not specified, it is assumed to be the structural type it-
self. We use the meta-variable R to refer to a structural type. Addi-
tionally, a type may be an intersection type, a singleton type (that
depends on a path), the top or the bottom of the subtype lattice, or
an un-type. Finally, we introduce T , which stands for the result
of refining all of T ’s un-members with unknown terms and types.
We will discuss this construct in more detail in Section 2.2.3.
Figure 2 defines the shape of kinds, paths, values, and the typing
context Γ. A path is a chain of member selections that starts with a
variable or an instantiation expression new T, which represents an
object. We mainly restrict the shape of paths to simplify the proofs
in the meta-theory.
2.2 Core concepts
Before describing the rules that define computation and classifica-
tion in Scalina, we build up intuitions about the core concepts that
underlie these mechanisms.
2.2.1 Members and un-members
Members are the liaisons between the different levels: a type de-
scribes the value members that may be selected on the terms it
classifies, as well as the type members that may be selected on the
type itself. The description of a member consists of the label of the
member, the classifier of the entity it stands for and – if the member
is concrete – the actual entity it is bound to (its right-hand side, or
RHS). For value members, the classifier is a type and the RHS is
a term, and type members specify the kind that classifies the type
they are bound to.
Scalina’s un-members are a more radical departure from Scala.
Un-members are used to encode parameterisation: they are place-
holders for members that must be provided by the client of the ab-
straction, much like the arguments of a function. Un-members are
turned into normal members using member refinement, which cor-
responds to passing arguments to a function. An entity with multi-
ple un-members is the equivalent of a curried function: refining one
of the un-members results in an entity with one less un-member to
be refined. Once all un-members have been refined, the member
representing the function’s result may be selected to complete the
application. This constitutes the essence of computation – on terms
as well as types – in Scalina.
Members and un-members can be seen as the two halves of
the contract specified by a classifier: members are available to the
client, whereas it must supply the un-members. Note that abstract
members have different semantics from un-members: an abstract
member is made concrete using composition within a subtyping
hierarchy, while an un-member is to be supplied by an external
client. A type with abstract members cannot be instantiated. An
abstract type can however be constrained (using the kind Concrete
(R)) so that it does not contain any abstract members.
2.2.2 Terms
The canonical form of a term is an object. For syntactic economy,
and since Scalina does not model effects yet, an object is repre-
sented by the instantiation of a type without abstract members.
Conceptually, an entity is just a vessel for denoting to which en-
tity each of its members – as described by the entity’s classifier
– is bound. Thus, an object contains mappings (from a label to a
term) for all of the members specified in its type. Operationally,
un-members can be thought of as members that are simply absent
from this mapping.
2.2.3 Types
If, on the term level, parameterising over functions is useful,
doing the same on the level of types sounds like an obvious
thing to do.
Erik Meijer