for First-Class Classes ∗

Asumu Takikawa . Stephen Strickland Christos Dimoulas Sam Tobin-Hochstadt Matthias Felleisen PLT, Northeastern University asumu, sstrickl, chrdimo, samth, matthias @ccs.neu.edu { }

Abstract 1. Untyped Object-Oriented Style Dynamic type-checking and object-oriented programming The popularity of untyped programming languages such as often go hand-in-hand; scripting languages such as Python, Python, Ruby, or JavaScript has stimulated work on com- Ruby, and JavaScript all embrace object-oriented (OO) pro- bining static and dynamic type-checking. The idea is now gramming. When scripts written in such languages grow and popularly called gradual typing [27]. At this point, gradual evolve into large programs, the lack of a static type disci- typing is available for languages pline reduces maintainability. A programmer may thus wish such as Racket [33, 34], for object-oriented languages such to migrate parts of such scripts to a sister language with a as Ruby [12] or Thorn [38], and for [23] on the static . Unfortunately, existing type systems nei- .NET platform. Proposals for gradual typing also exist for ther support the flexible OO composition mechanisms found JavaScript [19] and [31]. Formal models have validated in scripting languages nor accommodate sound interopera- soundness for gradual type systems, allowing seamless in- tion with untyped code. teroperation between sister languages [22, 27, 32]. In this paper, we present the design of a gradual typing system that supports sound interaction between statically- (define drracket-frame% and dynamically-typed units of class-based code. The type (size-pref- system uses row polymorphism for classes and thus supports (searchable-text-mixin mixin-based OO composition. To protect migration of mix- (searchable-mixin ins from typed to untyped components, the system employs a (status-line-mixin novel form of contracts that partially seal classes. The design (text-mixin comes with a theorem that guarantees the soundness of the (editor-mixin type system even in the presence of untyped components. (standard-menus-mixin frame%)))))))) Categories and Subject Descriptors .3.3 [Programming Languages]: Language Constructs and Features—Classes Figure 1. Abbreviated code with a chain of and objects Unfortunately, no existing gradual type system supports General Terms Languages, Design the full range of object-oriented styles found in scripting languages. These untyped languages tend to support flexible Keywords gradual typing, first-class classes, contracts, mechanisms for class composition, such as mixins or traits, sealing, design by contract, row polymorphism, blame theo- that allow the programmer to abstract over inheritance. Fur- rem (proof technique) thermore, some untyped languages support a generalization of mixins and traits where classes are first-class values and ∗ Supported in part by two NSF grants, the DARPA CRASH program, and thus can inherit from other classes at runtime. For example, a grant from the Mozilla Foundation. the implementation of the DrRacket IDE [8] makes exten- sive use of layered combinations of mixins to implement text editing features, as seen in the abbreviated example given in figure1—the full code uses 17 mixins. Permission to make digital or hard copies of all or part of this work for personal or In such languages, class composition requires the pro- classroom use is granted without fee provided that copies are not made or distributed for profit or commercial advantage and that copies bear this notice and the full citation grammer to reason about the specialization interfaces [20] on the first page. To copy otherwise, to republish, to post on servers or to redistribute of superclasses. A faithful type system must enable the pro- to lists, requires prior specific permission and/or a fee. OOPSLA ’12 Oct. 19, Tucson. grammer to express this reasoning via types. Meanwhile, a Copyright 2012 ACM [to be supplied]. . . $10.00 gradually typed language should support the exchange of "editor.rkt" import widget% from "gui.rkt" (define (modal-mixin base%) (define editor% (class base% (class widget% (super-new) (super-new) (field [mode ’command]) (define/public (on-key key) (define/public (toggle-mode) (case key (if (eq? mode ’command) [("C-x")(do-cut)] (! mode ’insert) . . . )) (set! mode ’command))) (define/public (on-paint)...) (define/override (on-key key) (define/public (save-file name)...) (if (eq? mode ’command) (define/private (do-cut)...) (on-key/cmd key) . . . )) (on-key/ins key))) . . . ))

Figure 2. Editor module classes between typed and untyped components while en- Unfortunately, Typed Racket does not currently support forcing the type system’ invariants. This desideratum im- first-class classes and the programming patterns they sup- plies protecting cross-component inheritance. port, particularly mixins and traits. While these program- To address these issues, we propose a novel gradual type ming patterns are widely used, they greatly complicate grad- system that properly validates a programmer’s reasoning in ual typing. To illustrate the problems, we present a series of the presence of both dynamic class composition and inheri- examples using (mostly) Racket syntax. The scientific reader tance across untyped-typed boundaries. Our design rests on will easily recognize similar problems in related languages. two technical results. First, we introduce partial run-time sealing as a mechanism to protect inheritance-related in- 2.1 Programming with First-Class Classes variants at the boundary between typed and untyped com- The architectures of GUI toolkits showcase the benefits of ponents. Second, we utilize row polymorphism to express OO programming, including the use of dynamic class com- constraints on class composition at component boundaries. position. Our goal is, however, to expose the pitfalls of mi- We present examples motivating the design in section2. grating untyped OO code to a typed world. To that end, we Our design comes with a formal model, which we outline start with an untyped sample for text editing wid- in section3. We address soundness for the system, including gets and then migrate some the code to an evolving explicitly typed-untyped interaction, in section4. 1 typed dialect of the language.

2. Typing & Protecting First-Class Classes Classes in Racket The editor%2 class in figure2 extends We use Racket—a language with first-class classes [11]—to widget%, a base class provided by the GUI library. The new present our design. Our choice of language is motivated by sub-class uses super-new to call the constructor of widget%. our practical experience with first-class classes in Racket and It defines three new public methods: on-key for handling key the availability of Typed Racket [33], a gradually typed sister events, on-paint for drawing the contents to the screen, and language of Racket. While our design is developed with save-file for saving the contents of the editor. Racket in mind, many of the lessons learned should apply to The first position in the class form, which specifies the other languages with dynamic class and object composition, superclass, is notably not limited to names of classes. We such as Ruby, Python, or JavaScript. can supply any expression as long as it evaluates to a class. In Typed Racket, untyped and typed code interoperate at This feature allows the definition of runtime mixins. the granularity of modules. Interactions between modules are mediated by higher-order behavioral contracts [9]. That Mixins A mixin is simply a function that take a class as is, when exporting values to untyped modules from typed an argument and subclasses it using the class form. With modules, the language turns types into appropriate contracts. mixins, programmers may parameterize classes over base In a typed module, the programmer annotates imports from classes and thus decouple features from specific points in untyped modules with types, that are enforced at runtime via class hierarchies. The right-hand column in figure2 presents contracts. a sample mixin that adds simple modal editing—as in vi— to an arbitrary editor. 1 For the complete definitions and proofs, see the technical report version of this paper [30]. 2 By convention, class names are suffixed with % in Racket. Concretely, modal-mixin adds a mode field that controls It defines a typed variant of the editor% class from fig- which keybindings are active, initially set to the command ure2. The revised definition is like the untyped one but mode. The mixin explicitly overrides the on-key method so adds type annotations to methods. Width on class that it dispatches key handling to the appropriate method.3 types allows casting a class to a narrower type, i.., the We can apply the mixin to create a concrete modal editor: type system can forget methods or fields. If the type sys- tem allowed width subtyping on class types, typed-editor% (define modal-editor% (modal-mixin editor%)) could be downcast to T = (Class ([on-paint : ( Void)])). Not all applications of modal-mixin to base classes are suc- This is fine for a client that uses the instances→ of class, as cessful. For example, if the base class already implements a the instances would have the type (Object ([on-paint : ( toggle-mode method or lacks an on-key method, then mixin Void)])) and therefore support only calls to the on-paint→ application fails with a runtime error. method. However, inheritance is now unsound because the In other words, public method definitions require the type system has no knowledge of whether methods are actu- absence of the method in the superclass while override ally present or absent. definitions require their presence. Our type system must Thus, the following definition type-checks but raises a express these restrictions in order to accommodate mixins runtime error because a public method is redefined: or other dynamic patterns of composition. As defined, modal-mixin can be applied to any class that (define piano% lacks the method toggle-mode and has the method on-key. (class (cast T typed-editor%) ;; subsumption Thus, modal-mixin’s functionality is not tied to a particular (super-new) class hierarchy and is composable with other editor features. ;; already defined in superclass More concretely, we can compose modal-mixin with other (define/public (on-key [key : Note]) mixins from our editor library to produce several combina- : Void tions of functionality: (play-sound key)))) (define ide-editor% In short, naive width subtyping for class types is unsound (modal-mixin with respect to Racket’s inheritance semantics; even for (line-num-mixin (indent-mixin editor%)))) Java, the naive approach would fail because a subclass may (define word-processing-editor% override a method with a different type. (modal-mixin (spelling-mixin (search-mixin editor%)))) Row polymorphism Another possibility is to repurpose row polymorphism In short, mixins are polymorphic with regard to the special- [36], studied in the context of type infer- ization [20] that the mixin expects. With this in ence for objects [26, 36], object calculi [10], and extensible mind, we need to consider how to accommodate the pro- records [13, 18]. Functions over extensible records also re- gramming patterns we have just discussed in a typed setting. quire polymorphism over row members with constraints on the presence or absence of members. Unlike subtyping, row 2.2 A First Design for Typed First-Class Classes polymorphism prohibits forgetting class members. Instead, From our previous discussion, we can identify two require- row polymorphism for classes abstracts over the features of ments for a typed sister language: it must support class poly- a class type—both method signatures and types of fields— morphism and its types must express constraints on method using constraints on row variables to express absence and presence and absence. One possibility is to use a type system row signatures to express presence. with bounded polymorphism and subtyping for class types. With row polymorphic types, the type of modal-mixin This choice is problematic, however, because runtime inher- from figure2 could be written as follows: itance and subtyping are at odds. ( ( / on-key toggle-mode)) For example, consider this code snippet: ∀((Class ([on-key : (String Void)] r)) → | (define typed-editor% → (class widget% (Class ([toggle-mode : ( Void)] → (super-new) [on-key : (String Void)] r))))]) → | (define/public (on-key [key : String]) : Void The mixin’s type is given as a that is poly- (case key morphic in a row variable , which, in turn, is constrained [("C-x")(do-cut)] . . . )) r to lack and members. These particular (define/public (on-paint) : Void ...) on-key toggle-mode constraints are needed because is a placeholder for the rest (define/public (save-file [name : String]) : Void ...) r of the methods or fields, which should contain neither (define/private (do-cut) : Void . . . ) . . . )) on-key nor toggle-mode. If r has an on-key method, it conflicts with 3 Like C], Racket requires that override methods be explicitly identified. on-key in the argument class that the mixin overrides. Since def/pub (m1 x) ... def/pub (m1 x) ... def/pub (m1 x) ... Classes def/pub (m2 x) ... def/pub (m2 x) ... def/pub (m2 x) ... m : c c m : c c m : c c 1 1 7→ 2 1 1 7→ 2 1 1 7→ 2 Contracts

(m1 o) X (m1 o) X (m o) (m o) Clients 2 X 2 × Transparent Opaque Sealing

Figure 3. Kinds of class contracts modal-mixin adds toggle-mode to its base class, whose type agrams for three different ways of dealing with methods contains r, the row variable r must also lack toggle-mode. or fields not included in a class contract. Strickland and Row variables are instantiated like ordinary type vari- Felleisen’s contracts [29] are transparent: they allow un- ables. Applying a row abstraction to a concrete row type re- mentioned methods or fields to pass through without any en- quires that the row satisfies the constraints on the variable. forcement. Our design requires two additional mechanisms Thus, the variable r above can be instantiated with the con- for handling unmentioned behavior in order to fully protect crete row ([on-paint : ( Void)] [insert : (String Void)]) typed code: opaque class contracts and sealing class con- but not with the row ([→on-key : (String Void)])→ or a row tracts. Opaque contracts disallow any unmentioned behavior that contains this signature, due to the constraints→ on on-key. by raising a contract error at the boundary. Sealing contracts By using row polymorphism, we make a tradeoff with allow unmentioned behavior to pass through the boundary subtyping. Class abstractions specify polymorphism explic- but require a matching unsealing to access it. itly in their type, rather than allowing the use of the sub- sumption rule. That is, we shift the burden of typing to the "typed-editor.rkt" implementor of a mixin. This does not complicate the ap- plication of mixins because, in practice, type application of import editor% from "editor.rkt" with type: rows can be inferred [35]. In contrast, our type system does (Class [on-key (String Void)] allow width subtyping on the types of objects, since objects [on-paint ( Void→)] . . . ) in Racket cannot be extended after creation. → In other words, our approach is pay-as-you-go. Program- (send (new editor%) on-key "C-x") mers need to be aware of row polymorphism only when deal- ing with abstractions that are not provided in most typed object-oriented languages, such as mixins. Meanwhile, pro- Opaque contracts Suppose we import the editor functional- grammers need not consider row polymorphism when deal- ity from the untyped module in figure2 into the nearby typed ing with objects, and can instead reason about their programs module typed-editor.rkt. Note that we use a double border using familiar subtyping rules. to distinguish typed modules from untyped ones. Since the editor class is untyped, we must specify an import type for 2.3 Protecting Typed Code from Untyped Code the class so that Typed Racket can type check its uses. The Types are only half of the gradual typing story. Runtime given class type specifies the signatures of the individual monitoring of type system invariants is the other half, which methods. To protect this import, a natural choice would be to we handle with higher-order contracts [9, 29] in the style of translate the type into a class contract that checks matching Tobin-Hochstadt and Felleisen [32]. Every type for a class predicates. Unfortunately, the existing class contracts imple- becomes a run-time contract at the boundary where it flows ment transparent behavior, i.e., they require that the meth- from a typed module to an untyped module or vice versa. A ods mentioned in the contract have the specified behaviors contract for first-class classes specifies clauses that protect but make no guarantees about any unspecified methods. methods and fields of the contracted class. Class contracts Hence, transparent contracts cannot protect typed code are specified separately from the definition of the class, since properly, even in this simple case. Recall that editor% in fig- classes are values and are not defined in a static hierarchy. ure2 implements a method that is absent from its import Our contract-based approach does not scale smoothly be- type, namely save-file, and thus its corresponding transpar- cause we wish to formulate types that are polymorphic in ent import contract allows the method to pass through. In the method and field clauses. Figure3 presents illustrative di- code fragment labeled "typed-editor.rkt" (continued), this would result in a name conflict in the typed module because out the application of the correct version of modal-mixin to the type system does not know that editor% provides save- a base class with methods other than on-key. file. That is, the code would type-check but signal a runtime error blaming the typed module for an error that the type "editor.rkt" (revised) system seemingly ruled out. ... (define (modal-mixin base%) "typed-editor.rkt" (continued) (class base% ... (super-new) (define my-editor% . . . ;; as above (class editor% (define/public (switch-mode)...) (super-new) . . . )) ;; error: save-file must be absent in editor% (define/public (save-file [filename : String]) : Void Instead, we introduce and use sealing contracts, which . . . ))) prevent the addition or use of sealed methods until a cor- responding unsealing contract is applied. Sealing contracts use unforgeable keys generated at run-time to prevent unau- To avoid this safety gap, we introduce and use opaque thorized access. This prevents modal-mixin from adding un- contracts so that typed code cannot accidentally import specified methods but allows unspecified methods from the classes that contain unknown methods. Note that the intro- base class to flow through mixin application. In other words, duction of opaque contracts and the prohibition on subtyping sealing contracts for mixin types reflect their key feature, for class types are due to the same underlying cause, namely polymorphism over base types. the desire to retain familiar inheritance semantics. Metaphorically speaking, sealing contracts establish a "typed-modal.rkt" private channel through one component to another. We use such a channel to send a typed class through an untyped import editor% from "typed-editor.rkt" mixin, ensuring that the mixin cannot tamper with the pro- import modal-mixin from "editor.rkt" with type: tected names of the class en route. A sealing contract at a ( (r / on-key toggle-mode) negative position (e.g., function argument) establishes an ∀ ((Class ([on-key : (String Void)] r)) entrance to the channel. Dually, a contract in a positive po- → | sition (e.g., function result) establishes an exit. The ends of → (Class ([toggle-mode : ( Void)] the channel are locked with unforgeable keys, allowing only → [on-key : (String Void)] r)))) authorized code to send and receive values on the channel. → | Our system seals classes because class types are poly- (modal-mixin% editor%) morphic. Classes that pass through sealing contracts are not completely inaccessible, however. Instead, sealing is applied at the granularity of class members such as methods and Sealing contracts For every feature in our type system, we fields. Attempts to invoke a sealed method name, access a need a corresponding feature in the contract system to en- sealed field name, or extend a class using a sealed name all force its invariants at runtime. Thus, we need some form of fail with a contract error. In contrast, sealing contracts do not parametric contract to protect row polymorphic functions on impose any access limitations on exposed methods. This es- classes. Consider what happens when an untyped mixin is tablishes the connection with row polymorphism: class types imported into the typed module "typed-modal.rkt" below. allow the use of concrete members at the specified type but This module imports modal-mixin from figure2 with a para- disallow the use of abstract members from a row variable. metric type and calls the mixin on the editor% class from Like most OO languages, Racket also contains stateful "typed-editor.rkt". Assuming modal-mixin is defined prop- operations. In general, the combination of polymorphic con- erly, no runtime error can blame the typed module. tracts and state requires some care to ensure soundness. In Suppose, however, that the programmer who wrote "ed- particular, the precise timing for key generation on seals itor.rkt" adds another public method to modal-mixin, as in is crucial. One choice is to generate keys for seals when a the revised version of "editor.rkt". Even if the creator of contract is applied, e.g., when an untyped value is imported typed-modal.rkt does not adapt the type to the new situation, into a typed module. For example, consider the situation in our system must discover the additional method and signal figure 4a. The "state.rkt" module exports state-mixin and a violation—otherwise the type system would be unsound. "broken-client.rkt" imports the mixin with a type that sug- One apparent option is to map the type to an opaque contract, gests it is the identity function on classes. Suppose that keys which prohibits the flow of modal-mixin across the module for seals are generated once as the mixin is imported. The boundary. Unfortunately, opaque contracts would also rule first time that state-mixin is called, editor% is sealed on en- "state.rkt" "search.rkt" (define (state-mixin base-class) (define (search-mixin base-class) (define storage #) (class base-class (cond [(not storage) (super-new) (set! storage base-class) (define/public (search string) base-class] ... [else storage])) ;; bad call to save-file (send this save-file 0))))

"broken-client.rkt" "search-client.rkt"

import state-mixin from "state.rkt" import editor% from "typed-editor.rkt" with type: import search-mixin% from "search.rkt" ( (r)(Class ( r)) (Class ( r))) with type: ∀ | → | ( (r / autosave) (send (new (state-mixin editor%)) ∀( (Class ( r)) on-key) → (Class | (send (new (state-mixin modal-editor%)) (search : (String Void) r)))) toggle-mode) → | (define searchable% (search-mixin editor%)) (send (new searchable%) search "x")

(a) Misuse of state () Invalid method invocation Figure 4. Potential violations of soundness via mixins try to the mixin and unsealed on exit. Sending the on-key typed message is thus acceptable, and no contract errors are raised. untyped The next line is a call to state-mixin, perhaps formu- editor% lated under the assumption that it behaves like the identity save-file function—a possibility suggested by its row-polymorphic type. This time, the mixin is applied to modal-editor%, which is sealed with the same key as editor%. This allows class/c - state-mixin to return editor% without triggering a contract { } error, and that cause the method missing error for toggle- mode. In short, even though the code type-checked in our searchable% imaginary type system, it caused a run-time type error. search To be sound, our system must generate these seals at each mixin invocation. This ensures that editor% and modal- editor% are sealed with distinct unforgeable keys and that class/c search : string? void? the second call to state-mixin signals a contract error. { → }

Object contracts are like class contracts, except that they (send obj search "x") transparently protect particular instances. Since object types offer width subtyping, contracts on objects need not enforce the of opacity that class contracts enforce. Figure 5. A snapshot of the class hierarchy after unsealing 2.4 Access restrictions for method invocation While opaque contracts ensures that untyped code cannot has a method search that assumes the existence of a save-file access methods unlisted in interfaces, dynamic uses of in- method in its base class. Moreover, search provides a faulty heritance (e.g., mixins or traits) create a potential hole in argument to save-file. When search is invoked on an instance this protection. Consider the example in figure 4b. An un- of searchable%, an error occurs due to that faulty argument. typed mixin imported into the "search-client.rkt" module Since the type given in the client module does not mention ∗ ∗ e ::= v x (e e) op(e) if e e e send(e, m, e) iget(f , e) iset!(f , e, e) new(e) (expressions) | | | | | | | | monl,l (c, e) class(e) f := v mpx. e mox. e | l | { } op ::= num? bool? (primitives) | v ::= #t #f n cv o λx. e (values) | | | | | cv ::= object% class/vι (cv) (f, v) mx. e (class values) | { } o ::= object/v(cv) (f, a) (objects) { } f ∗ ::= f a (field terms) | x Var (variables) ∈ f Field (fields) ∈ m Method (method names) ∈ a Location (locations) ∈ l, j, Label (contract labels) ∈ Figure 6. Untyped Expressions save-file and the class is unsealed by the time it is instan- baseline property of any type system, to be preserved even tiated, sealing contracts do not catch this faulty method in- when typed components are linked with untyped code. vocation. That is, a method invocation from untyped code to typed code can be unsafe and must be rejected unless a 3.1 Syntax contract protects the method. Our language, called TFCC, allows the embedding of typed To prevent faulty access via mixins, we require method terms in untyped terms and vice versa. The interactions calls to dispatch to either a method defined within the same between them are mediated by monitors with contracts. A component or a contracted method. That is, if untyped code monitored term can be thought of as a server module that invokes a method that is not protected by a contract, its exports services to its context, i.e., its client(s). This section definition must reside in untyped code as well. The same starts with a look at the untyped portion of the language condition applies to typed code. (figure6) and then proceeds to the typed portion (figure7). Figure5 illustrates this restriction graphically. The dia- gram shows the object interactions established by the code in Expressions include values, variables, applications, primi- figure 4b. There are two relevant component transitions here: tive operations, conditionals, method invocation, object in- one between the editor% class and the mixin from the un- stantiation, field mutation, contract monitors, and classes. typed component, and one between searchable%—created The set of values includes booleans, numbers, class values, by the mixin—and its use in the typed component. A con- objects, and λ-abstractions. Method invocations are written tract protects both boundaries, but the inner contract disal- send(e0, m, e1) where e0 is the receiver, m the method lows the call to because the contract specification save-file name, and e1 the argument to the method. does not include that method. The expression class(e) f := v mpx. ep mox. eo The problem with cross-boundaries access was discov- consists of a superclass expression{ e, field names f paired} ered during a first, failed attempt to establish the soundness with corresponding initial values, and definitions of public of our type system design. Conversely, the failure suggested methods mp and override methods mo. If the superclass ex- this constraint on protected method calls. Fortunately, this pression evaluates to a suitable value, a class reduces to a constraint does not reduce the expressiveness of purely typed class value class/v with a unique key ι that is used for or untyped code, but it requires that invocations that trans- method dispatch. The term object% represents the root of fer control from typed to untyped code (and vice versa) are the class hierarchy. Field declarations produce mutable local properly monitored by the contract system. fields, which are only accessible from within method bodies. We include state so that the calculus can express examples 3. Formalizing Typed First-Class Classes that introduce potential unsoundness in the absence of ap- Our examples have exposed thorny problems about pro- propriate dynamic sealing, as explained in section2. grams that use dynamic class composition across the bound- ary between typed and untyped components. To communi- State Fields make objects stateful. The iget and iset! cate our solution, we present a formal model of gradually- expressions, respectively, get and set field values locally. typed dynamic class composition. The model is both a vehi- cle for a compact presentation of our design and a platform Types include Int and Bool plus function types. An object for proving its soundness. We consider type soundness the or class type consists of rows, describing method signatures. σ ::= τ (ρ m).τ (type schemes) | ∀ \ τ ::= Int Bool Class er Object r τ τ (types) | | | | → r ::= (m : τ) (rows) { } er ::= r (m : τ) ρ (extended rows) | { | } eτ ::=( eτ eτ ) x op(eτ ) if eτ eτ eτ Λ(ρ m). eτ eτ [r] send(eτ , m, eτ ) (typed expressions) ∗| | | ∗ | \ l,l| | iget(f , eτ ) iset!(f , eτ , eτ ) new(eτ ) mon (c, e) | | | | l p o class(eτ ) f : τ := vτ m (x : τ): τ eτ m (x : τ): τ eτ | { } vτ ::= #t #f n cvτ oτ λ(x : τ) eτ (typed values) | | | | | ι p o cv ::= object% class/v (cvτ ) (f, τ, vτ ) m (x : τ): τ eτ m (x : τ): τ eτ (typed class values) | { } o ::= object/v(cvτ ) (f, τ, a) (typed objects) { } Figure 7. Typed Expressions

For example, (on-key : τ1), (on-paint : τ2) is a row c ::= flat(op) c c (pre-contracts) { } | 7→ with labels on-key and on-paint and types τ1 and τ2. Rows ∀c(ρ m).(c c) class/c•([m c c]) in class types may be extended with a row variable as in | \ 7→ | 7→ class/c∗(ρ, [m c c]) object/c([m c c]) (on-key : τ1), (on-paint : τ2) ρ . With r1 r2, we | 7→ | 7→ {concatenate two rows. | } ⊕ e ::= ... blamel (expressions) | l Typed expressions require type annotations for all variable c ::= flat(op) c c (core contracts) | 7→ declarations. Since we abstract over rows, we also have type ∀c(ρ m).(c c) class/c•([m c c]) abstractions Λ(ρ m). eτ and type applications eτ [r]. | \ 7→ | 7→ \ seal/c([m c c], m, γ) | 7→ k,l unseal/c([m c c], γ) object/c([m c c]) Untyped-Typed Interaction A monitor monj (c, e) sepa- | 7→ | 7→ rates a program into components [5], i.e., e and the context γ ::= ς ρ (key terms) | of mon, mediated by the contract c. The superscript labels k ς Key (keys) ∈ and l name the server and client components respectively. The label j names the contract c. In our model, monitors v ::= ... ∀Gl,l(ρ m).(c c) v (values) play only one role. They mediate between typed and untyped | l \ 7→ { } Gl,l components. Hence it suffices to use just two labels, instead cv ::= ... l cv, (m c c) (class values) l,l| { 7→ } of unique labels per components: u for untyped, t for typed. SG cv, m, ς | l { } Our reduction semantics models exchanges of values be- k,l o ::= ... OGj o, [m c c] (objects) tween components with substitutions of monitored values, | { 7→ } which embeds typed values within untyped code and vice Figure 8. Contracts and guards versa. A typed-in-untyped embedding is valid if the moni- tor’s contract is related to the type of the embedded term; an untyped-in-typed embedding is valid if it type-checks in the Class contracts come in two varieties: opaque and sealing. typed context with the contract interpreted as a type. The bi- The former differs from transparent class contracts [29] in jective function , defined in figure9, specifies the natural that they enforce the absence of methods not mentioned T • correspondence betweenJK contracts and types. in a contract. This feature of class/c ensures that typed modules can safely import classes from untyped modules. ∗ Contracts check type-like properties at runtime. In contrast Sealing contracts class/c have meaning only within a to Eiffel, our higher-order contracts describe the behaviors row polymorphic contract. They are used to specify whether of entire objects, including their methods [9, 29]. a given contract position should be polymorphic. For a nega- The model’s contract language includes flat or predicate tive position, an elaboration from pre-contracts to core con- ∗ contracts, function contracts, class contracts, and contracts tracts translates class/c to a sealing contract seal/c; it for parametric functions. Figure8 presents the syntax of con- becomes an unseal/c contract in a positive position. Both tracts in two parts: pre-contracts are surface syntax, which seal/c and unseal/c contain γ, which is either a variable 4 are elaborated into (core) contracts. or a key for unlocking seals.

4 The variable case is required by our choice of semantics and occurs only in intermediate reduction steps. The runtime syntax also includes guards. Guards act like methods must all be well-typed in the usual sense. Method contract monitors but, unlike monitors, are values. Since bodies are checked under the assumption that the receiver guards are values, they can pass through contract boundaries. has type Object R er , where R er denotes the class’s row The parametric guard ∀Gk,l(ρ m).(c c) v behaves like but without the rowJ variableK if erJ containsK one. j \ 7→ { } a function. When applied, it generates a fresh seal key for its Object instantiation (T-NEW) requires that the instanti- contract. This ensures that keys cannot be forged using state, ated class has a concrete type, i.e., any type variables have as explained in section 2.3. been instantiated. Method invocation is checked by T-SEND. Since contract checking for a class is delayed until its Since objects have concrete rows, an object type is a k,l methods are invoked, we use Gj cv, (m c c) to retain subtype of another if the corresponding rows are subtypes the contracts in the class hierarchy.{ Similarly,7→ the} sealing via the standard width subtyping rule.5 Meanwhile, there are l,k 0 guard SGj cv, [m c1 c2], m , ς wraps a class cv in or- no subtyping rules for classes because row polymorphism der to retain{ seals until7→ they are checked.} replaces subtyping for classes, as detailed in section2. We defer rules for interactions between typed and un- typed code to section 4.3, where we explain the soundness T flat(int?) = Int theorem for mixed type programs. J K T flat(bool?) = Bool J K 3.3 Operational Semantics T c c = T c T c 1 7→ 2 1 → 2 c J K J K J K Our operational semantics uses context-sensitive reduction T ∀ (ρ m).(c1 c2) = ρ m.T c1 c2 J \ 7→ K ∀ \ J 7→ K rules [7]. Figure 12 presents the evaluation contexts for our T class/c•([m c c ]) = language and lists the reduction rules, which in turn rely 1 7→ 2 J K on the metafunctions defined in figure 13. The reflexive- Class (m : T c1 c2 ) { J 7→ K } transitive closure of the reductions determines the evaluation T seal/c([m c1 c2], m, ρ) = function. J 7→ K We first explain the semantics of the base language with- Class (m : T c1 c2 ) ρ { J 7→ K | } out contracts and then incrementally introduce the cases for T unseal/c([m c1 c2], ρ) = contract monitoring. The typed language has the exact same J 7→ K Class (m : T c1 c2 ) ρ semantics as the untyped one. For some purposes, we as- { J 7→ K | } sume that the types are first stripped from typed expressions; T object/c([m c1 c2]) = J 7→ K in other cases, we assume that the reduction system carries Object (m : T c1 c2 ) type information without using it. { 7→ } J K The reduction rules in figure 11 define the conventional Figure 9. Type-contract correspondence behavior of λ-expressions, primitives, and conditionals; we omit the obvious definition of δ. The semantics of first-class classes is straightforward as well. The evaluation contexts 3.2 Type System ensure that class expressions reduce only after the superclass The type system is based on a typed λ-calculus with subtyp- expressions are reduced to values. A class successfully re- ing, limited mutable variables, object types, and class types. duces to a value (CLASS) when all of its override methods The important typing rules are shown in figure 10. The rules are present in the superclass and its public methods absent. use a judgement Γ Σ e : τ that states that a term e has Method invocation (SEND) triggers only if the given type τ assuming free| variables` are typed in Γ and store loca- method m is present in the class hierarchy of the receiving tions in Σ. Store typings are used to type-check operations object. The rule relies on a metafunction to look up the tar- on the private fields of objects. get method in the class hierarchy of the receiver object. The Row abstractions are checked using T-ROWABS. Appli- Pull function traverses the hierarchy to locate the method. cations of abstractions to rows (T-ROWAPP) require that the The rules for state are conventional. Creating a new ob- provided row matches the absence labels on the abstraction’s ject from a class (NEW) chooses unallocated addresses in bound variable using the Γ ρ m judgement. the store and reduces to an object value. Similarly, getting The interesting typing rules` \ are those involving class and and setting fields (GET,SET) just involves looking up and object types. A class is well-typed (T-CLASS) if its super- replacing values in the store. class is a valid class and allows extension with the new pub- Figure 12 introduces contract monitoring. Since reduc- lic and override methods. These conditions are ensured by tions may result in contract violations, reductions may pro- l the judgements that a row lacks a member, Γ er m, and duce the error term blamej that pinpoints the misbehaving that a row has a member, Γ m er. If the` superclass\ ` ∈ type contains a row variable, then all methods must be com- 5 Depth subtyping is omitted for simplicity, but we conjecture that its addi- patible with the absence labels on the variable. Fields and tion poses no problems. Γ Σ e : τ | `

T-ROWABS T-ROWAPP Γ, (ρ m) Σ e : τ Γ Σ e : (ρ m).τ Γ r m \ | ` | ` ∀ \ ` \ Γ Σ Λ(ρ m). e : (ρ m).τ Γ Σ e [r]:[r/ρ] τ | ` \ ∀ \ | ` T-SEND Γ Σ e0 : Object r T-NEW | ` T-ROOT (m : τ1 τ2) r Γ Σ e1 : τ3 τ3 <: τ1 Γ Σ e : Class r → ∈ | ` | ` Γ Σ object% : Class Γ Σ send(e , m, e ): τ Γ Σ new(e): Object r | ` {} | ` 0 1 2 | ` T-OBJECT Γ Σ cv : Class r i, Σ(ai) = τi | ` ∀ Γ Σ object/v(cv) (fi, τi, ai) : Object r | ` { } T-CLASS p o Γ Σ es : Class ers Γ ers mi Γ mi ers | ` p `p \ ep ` p ∈ Γ, this : τobj, xi : τ1i f : τf Σ i : τ2i o o | ` eo o Γ, this : τobj, xi : τ1i , f : τf Σ i : τ2i p p p| ` Γ vf : τf er = ers (m : τ τ ) τobj = Object R er ` i i ⊕ { 1 → 2 } p p p p p o o o o o J K Γ class(es) f : τf := vf m (x : τ ): τ e m (x : τ ): τ e : Class er ` { 1 2 1 2 }

Figure 10. Selected type rules

E ::=[] (E e) (v E) op(E) if E e e send(E, m, e) send(v, m, E) igetι(f ∗,E) | | | | | | | iset!ι(f ∗,E, e) iset!ι(f ∗, e,E) new(E) monl,l(c, E) class(E) f := v mx. e mx. e | | | | l | { } E[ ],S , E[ ],S h ··· i → h ··· i (λx. e v) [v/x] e BETA · op(v) δ(op, v) DELTA · if #t e e e IFTRUE 2 3 · 2 if #f e e e IFFALSE 2 3 · 3 ι class(cv) f := v mpxp. ep moxo. eo . class/v (cv) (f, v) mx.e CLASS if mp Methods{ (cv) = and mo Exposed} (cv) { } ∩ ∅ ⊆ and where ι is fresh and mx.e = mpxp. [/idι] ep moxo. [/idι] eo ⊕ h i send(o, m, v) . ( this/ eo, a/f e v) SEND if m Methods(o) ∈ and where e = Pull(o, m) and (f, a, ι) = ObjectFields(o)

E[new(cv)],S E[object/v(cv) (f, a, ι) ], [v/a] S NEW h i · h { } i where (f,v,ι) = Fields(cv) with a dom(S) = ∩ ∅ E[igetι(a, o)],S E[v],S GET h if (f, a, ι) ObjectFieldsi(o) and where v = ·S(a) h i ∈ E[iset!ι(a, o, v)],S E[v],S [a/v] SET h if (f, a, ι) ObjectFieldsi(o) · h i ∈ Figure 11. Reductions for the base language component l as violating contract j. Blame is propagated The contracts contained in a parametric contract are either through all evaluation contexts. sealing or unsealing contracts. A sealing contract checks that Contracts control how monitors behave. Depending on all of the contracted methods are present in the class and the kind of contract, monitors reduce to additional moni- reduces to a seal guard. Similarly, an unseal contract reduces tors or guards, which immediately check the contract or de- to an unsealed class protected with a guard. lay checking in higher-order cases. When immediate checks The semantics of sealing requires additional side condi- fail, contracted values reduce to contract errors, blaming a tions for the class and method invocation cases. For class specific component l for violating some fixed contract j. expressions, the semantics requires that none of the public The reduction rules for monitors deserve special atten- or override methods are sealed in the superclass. tion. Monitoring an immediate contract reduces to a condi- Let us illustrate the operational semantics of sealing with tional expression (FLATC). If the predicate fails, a contract a sample reduction sequence based on the example in fig- error is signalled. A monitor for a function contract reduces ure 4b. In that example, a typed program invokes the search to a wrapped function (FUNC), which monitors argument method on an object whose class was constructed by the ap- and result contracts with appropriate blame [9]. plication of an untyped but monitored mixin:

Monitors with class or object contracts reduce to guards u,t that wrap the appropriate classes or objects for higher- c = monj (ctc, mixin) editor% order contract checking [29]. Since our class contracts are e = send(new(c), s, "x") opaque, they enforce the additional constraint that the con- mixin = λc. class(c) s x. send(this, sf, 0) ) ctc = ∀c(ρ ).(seal/c{ ([ ] , , ρ)) −} tracted methods are the actual methods of the protected class \− − ∅ (CLASSC). This constraint protects against the invocation of 7→ unspecified methods, which would otherwise violate safety unseal/c([s sctc] , ρ) as illustrated with the "typed-editor.rkt" example in sec- where sctc = flat(string?) flat(void?) tion 2.3. Similarly, object contracts ensure that the protected 7→ object actually has the contracted methods (OBJECTC). The mon term monitors the module import that embeds the When monitors create guards, the latter track the con- mixin into its use in the typed component with a contract ctc. tracts that need to be carried along for method invocation. The model encodes the untyped mixin from that example as Thus, the Pull metafunction delivers more than a method a function from class c to an extension with method s—short definition. When looking up a method, attaches the ap- Pull for search. The contract ctc corresponds to the type in the propriate contracts to the method as they are discovered in example where sctc is the corresponding contract for s. the class hierarchy. If succeeds, the call reduces to the Pull The monitored mixin is applied to the editor% class, wrapped expression applied to the argument value. meaning the reduction starts from this evaluation context: Two other details are necessary to understand method invocation. The receiver of a method call (i.e., this) is send(new([ ] editor%), s, "x") wrapped in contracts found between the method definition and the caller by ProtectThis. Doing so ensures that dynam- First the monitor itself reduces to a guarded function that ically dispatched calls are protected with the expected con- installs a sealing contract on application. Next we reduce tracts. Furthermore, the semantics also ensures that meth- the application of the guarded mixin to the editor%, sealing ods can be invoked only if both the caller and method are in editor% as it is substituted in the mixin’s body. the same component. This prevents a mixin from invoking Here is the result of these first few reduction steps: a method without protection, preventing the faulty invoca- u,t tion from the search-mixin example in section 2.3. In other monj (unseal/c([s sctc] , ς), class/v(SGt,u editor%, [ ] , , ς ) words, all method invocation must either be dispatched lo- j { − ∅ } sc x. send(this, save-file, 0) ) cally or through a contract monitor. { −} The remaining contract forms track sealing and unsealing The superclass of the class value is a sealed version of the of methods in order to ensure sound interactions between editor% class; the unsealing contract remains. typed and untyped code in the presence of functions that The unsealing yields a guarded class: manipulate classes. Such a function must be monitored by a parametric contract, which reduces to a parametric guard u,t u,t G class/v(G editor%, [ ] ) ( C). The guard behaves like a function, but with additional j { j { − } { ∀ sc x. send(this, sf, 0) , monitoring ( G-APP). An application of the guard results in } ∀ [s sctc] the generation of a seal key ς. It is crucial, as we demon- } strated through the broken-mixin in subsection 2.3, that ev- Finally, this guarded class is instantiated as an object ery application is sealed separately in order to prevent viola- containing the class. At this point, the method invocation of tions of row parametricity due to state. s is evaluated, which sets up the body of s as the redex. The body, in turn, calls the sf method. This method invocation E[blamek],S , blamek,S h j i → h j i E[ ],S , E[ ],S h ··· i → h ··· i ι class(cv) f := v mpxp. ep moxo. eo . class/v (cv) (f, v) mx.e CLASS if mp Methods{ (cv) = , mo Exposed} (cv), mp NotSealed(cv) { } ∩ ∅ ⊆ ⊆ and where ι is fresh and mx.e = mpxp. [/idι] ep moxo. [/idι] eo p p p o o o ⊕ l class(cv) f := v m x . e m x . e blamej CLASSERR { p }· o if HasBarrier(cv, m), mi Methods(cv) = , mi Methods(cv) and where m = mp m∩o and (l, j) = LocateBarrier∅ ⊆ (cv, m) ⊕ h i send(o, m, v) . ( this/ eo, a/f e v) SEND if m Methods(o) and SameOwner(o, m, , ) ∈ l ⊥ ⊥ l l and where e = Pull( o , m), eo = ProtectThis( o , m, o ), | | | | | | and (f, a, ι) = ObjectFields(o) k send(o, m, v) . blamej SENDERR if m Methods(o), and not SameOwner(o, m, , ) and where∈ (k, j) = OwnerLimit(o, m, , , ) ⊥ ⊥ ⊥ ⊥ ⊥ k,l k monj (flat(op), v) if op(v) v blamej FLATC k,l · k,l l,k monj (c1 c2, v) λx.monj (c2, (v monj (c1, x))) FUNC k,l 7→ • · k,l mon (class/c ([m c c ]), cv) G cv, [m c c ] CLASSC j 1 7→ 2 · j { 1 7→ 2 } if m = Exposed(cv) k,l • mon (class/c ([m c c ]), cv) blamek CLASSCERR j 1 7→ 2 · j if m = Exposed(cv) k,l 6 k,l mon (object/c([m c c ]), o) OG o, [m c c ] OBJECTC j 1 7→ 2 · j { 1 7→ 2 } if mi Exposed(o) k,l ⊆ mon (object/c([m c c ]), o) blamek OBJECTCERR j 1 7→ 2 · j if m Exposed(o) k,l 6⊆ 0 l,k 0 monj (seal/c([m c1 c2], m , ς), cv) SGj cv, [m c1 c2], m , ς SEALC 7→ 0 · { 7→ } if mi Exposed(cv) and m m Exposed(cv) = k,l ⊆ \ 0 ∩ ∅ k monj (seal/c([m c1 c2], m , ς), cv) blamej SEALCERR 7→ 0 · if mi Exposed(cv) or m m Exposed(cv) k,l 6⊆ \ ⊆ k,l mon (unseal/c([m c c ], ς)ρς, cv) G Unseal(cv, ς), [m c c ] UNSEALC j 1 7→ 2 · j { 1 7→ 2 } if Sealed(cv, ς) and mi Exposed(cv) k,l ⊆ mon (unseal/c([m c c ], ς)ρς, cv) blamek UNSEALCERR j 1 7→ 2 · j if not Sealed(cv, ς) or mi Exposed(cv) k,l c 6⊆ k,l monj (∀ (ρ m).(c1 c2), v) ∀Gj (ρ m).(c1 c2) v C \ 7→ · k,l \ 7→ { } ∀ (e v) (mon (c c ρ/ς , v ) v) G-APP · j 1 7→ 2 1 ∀ where e = ∀Gk,l(ρ m).(c c ) v and ς is fresh. J K j \ 1 7→ 2 { 1} Figure 12. Reductions for monitored terms name domain, range / purpose HasBarrier o or cv, m #t or #f checks if some7→ m is inaccessible due to a guard. LocateBarrier o or cv, m (l, l) returns the blame7→ labels for the closest inaccessible m in the object or class hierarchy. SameOwner o or cv, l or , l or #t or #f checks if m ⊥is owned⊥ by 7→ the calling context OwnerLimit o or cv, l or , l or , l or (l, l) returns the label⊥ of the⊥ calling⊥ 7→ context of m and of the contract boundary where ownership of m was lost Pull cv, m e returns7→m’s implementation as a λ-term wrapped with the necessary contracts ProtectThis o, m, o o traverses7→ the first object and its hierarchy to apply all contracts needed to protect the second object when it is a receiver of a call to m Sealed cv, ς #t or #f checks7→ if cv contains a seal guard that is locked with ς. Unseal cv, ς cv removes7→ seal guards locked with ς in the class hierarchy. Notsealed o or cv m returns unsealed7→ methods in the hierarchy. Exposed cv m returns7→ all the exposed method names in the hierarchy Methods o or cv m returns method7→ names in the hierarchy. Fields cv (f, v, ι) returns7→ the fields’ initial values in the class hierarchy. ObjectFields o (f, a, ι) returns7→ the object’s field values

Figure 13. Metafunctions are defined inductively on the structure of the first argument triggers the SameOwner metafunction with the sf method monitor [6], meaning components do not export their val- as an argument. This metafunction fails because the method ues without appropriate contract protection. Based on these lacks a contract between the method’s invocation on this two major steps, we finally show that typed components and the implementation in the editor% class in the hierarchy. of mixed-type programs cannot be blamed for violations of u Thus, the final result is blamej as expected. type invariants. 4.1 Type soundness 4. Type Soundness for Mixed Programs The reduction relation we use for typed terms is the relation Type soundness establishes a minimal logical standard for a for untyped terms in figure 12 except that the reductions . In this section, we use the formal carry along type annotations. Type applications are reduced model to prove that our design meets this criterion. Our analogous to function application, but types do not affect proof of soundness requires two steps. First, we must prove reductions in any other way. that the type system is sound with respect to the execution In this setting, typed programs cannot go wrong, in par- of typed programs. Second, we must show that mixing in ticular they cannot call undefined methods. untyped components does not violate the invariants of the Lemma 1. (Type Soundness) For all eτ , if eτ : τ, typed components. That is, the interpretation of types as either ∅ | ∅ ` contracts at component boundaries must prevent any type- ∗ • for all e such that eτ , , e ,S , there exists an like runtime error assignable to typed components. 1 h ∅i → h 1 1i For the proof of soundness, we employ the usual progress e2 such that e1,S1 , e2,S2 or, ∗ h i → h i 0 and preservation technique [37]. For the soundness of types • eτ , , vτ ,S where Σ vτ : τ for some Σ and h ∅i0 →0 h 0 i ∅ | ` complete Σ S and τ <: τ. as contracts, we show that the contract system is a ∅ | ` ing to the relation’s judgments, ownership can change only via monitors. When this happens, the ownership annotation ; l e Well formed programs G on the guarded expression must match the positive label of ; k; l c Well formed source contracts D B the monitor while the negative label should coincide with ; ; ; l e Well formed terms the owner of the context. The judgment does not allow any K G S k,l other ownership annotations that may change the owner of a ; ; k; l B c Well formed contracts term. The environment records the owner of each binding K D G S Well formed stores encountered by the judgments. K S ∼ The relation for well-formed programs relies on the def- inition of an additional judgment ; k; l B c that checks Figure 14. Relations for Well Formed Programs that contracts are well-formed. It picksD up the positive and negative labels of each monitor and ensures they coincide Recall from section 3 that Γ Σ S says store S is typable with the obligation annotations on the positive and negative | ` under Γ and Σ. As mentioned, the theorem requires two con- pieces of the contract of the monitor. After all, the client ventional lemmas: progress and preservation. The statement (negative label) of a guarded component is responsible for of both calls for a typing judgment applicable to intermedi- the values it consumes while the server (positive label) is ate states, i.e., states with non-empty stores for private fields. responsible for the values it produces. Other than that, the details are straightforward and omitted. Unfortunately, this strict distinction between negative and positive parties does not hold for classes and methods. The 4.2 Complete monitoring receiver of a method invocation is an object that, via sub- Complete monitoring [6] is a formal criterion for the correct stitution, traverses the class hierarchy from the call site to design of a contract system. It imposes two conditions on the method implementation. Depending on the direction of a correct contract system: complete mediation and correct this migration, the roles of clients and servers on the con- blame assignment. tract boundaries reverse. Thus, for each method contract the server and the client share responsibility for all pieces of Complete mediation requires that the contract system does the contract. We account for this codependence by allowing not allow values to pass between components without a obligation annotations to have sets of labels and by adjusting contract check—possibly the always-true contracts—where the judgments accordingly. contract monitors act as component boundaries. Put differ- Sealing and unsealing contracts pose further challenges ently, at any point in time every value is owned by one and for proving the correctness of the contract system. We guar- only one component. Intuitively, owner denotes the compo- antee that sealing contracts show up only in negative posi- nent that may affect the flow of the value (e.g., export the tions with respect to the corresponding row variable ρ by value to another component). If the owner wishes to share a marking sealing contracts with the annotation, ρ = . Sim- value with other components, it must do so under the aus- ilar annotations, ρ = +, decorate unsealing contracts.− The pices of a monitor or guard. Ownership of terms is formal- environment tracks the bound row variables and their po- ized via ownership labels. Expressions e are annotated with larity, + or D. The well-formed source contracts relation en- l − an owner label l, e.g., e . We use lo to indicate the implicit sures that sealing contracts appear in the right places. | | owner of the whole program. The judgments for well-formed programs and contracts provide the foundation for complete monitoring. Complete Correct blame assignment requires that upon contract fail- monitoring aims to establish that if each term in a program ure, the contract system blames the contract party responsi- has a single owner initially, then each term has a single ble for the breach of the contract. A party is responsible for a owner throughout the evaluation of the program. Embedded contract failure if a value it owns fails an immediate contract foreign terms can exist inside a host component, but only check and it is obliged to uphold that particular contract. if they are wrapped with a contract monitor. Accordingly, To indicate responsibility for immediate contract checks, we we change the reduction semantics to propagate ownership use obligation annotations. For instance, flat(op) is anno- annotations as values flow through the program. This change tated with obligation l as flat(op) l. If a component l has is independent of the contract system and does not affect b c an immediate contract as an obligation, it must ensure that the meaning of the program. Host components assimilate when a value crosses the boundary protected by this contract, values only when the values are primitive and satisfy their the contract is checked appropriately. contracts. In any other case, values accumulate more owners Complete monitoring assumes that component bound- as they flow from one component to another. aries agree with ownership annotations and that obligations If a contract system is a complete monitor, then all anno- match the labels on monitors. To check this relationship, we tations in a value’s stack of ownership labels must be iden- define a relation ; l e that ensures the well-formedness tical. Otherwise, the contract system would allow values to of e with respectG to a label l and environment . Accord- G cross component boundaries without appropriate protection. The two lemmas use a subject different than the judgment We make redexes that involve values with stacks of non- for well-formed programs. Even though the latter guarantees identical owners stuck states, which makes violations of the the absence of stuck states and correct blame, it is not gen- single-owner policy manifest. To differentiate between the eral enough to handle terms produced during evaluation. The latter stuck states and stuck states due to type errors we in- judgment for well-formed terms, ; ; ; l e, is an exten- troduce errork, dynamic type errors. Thus, a contract sys- sion of the relation for well-formedK G programsS that can ac- tem that is a complete monitor renders states unreachable if commodate intermediate terms such as objects, class, values they are stuck due to a value having multiple owners. and guards.

Definition 1. (Complete monitoring) A reduction relation Proposition 1. If ; l0 e0 then ; ; ; l0 e0. ∅ ∅ ∅ ∅ for TFCC is a complete monitor if ; l0 e0 implies: The new relation uses two additional environments, and → ∅ S • ∗ . The first, store coloring, records the owner of the contents e0, v, S or, K h ∅i →∗ h i k of each store location. Its function is similar to that of store • e0, error ,S or, h ∅i → h i typing in type soundness proofs. The relation S • for all e such that e , ∗ e ,S , there exists e 1 0 1 1 2 ensures that store coloring is synchronized withK the contentsS ∼ such that e ,S h e ,S∅i →or,h i h 1 1i → h 2 2i of the store. • if e , ∗ e ,S ∗ blamek,S , there exists h 0 ∅i → h 1 1i → h j 2i The second, key coloring, records the owner of each seal- k,l k e1 = mon ( flat(op) , v) and for all such e1, ing/unsealing key. Components obtain keys as they generate j b c v = v k and k k, them and each component should only seal and unseal val- | 1| ∈ e k,l • k and for ues with keys it owns. The environment guarantees that 1 = monj ( class/c ([m c1 c2]) , v) K b k 7→ c the keys stored in sealing guards have the appropriate owner. all such e1, v = v1 and k k or, | | ∈ In addition, key coloring checks whether contracts remain e = monk,l( object/c([m c c ]) k, v) and for 1 j b 1 7→ 2 c well-formed after row variables are replaced by keys. We all such e , v = v k and k k or, 1 | 1| ∈ specify this constraint with an extension of the relation for ρ=+ k,l e k,l k and well-formed contracts, ; ; k; l B c. It refers to the key 1 = monj ( unseal/c([m c1 c2], ς) , v) K D b k 7→ c coloring when it checks sealing and unsealing contracts. In for all such e1, v = cv and k k or, ρ=−| | ∈ these cases, the owner of the seal must match the party e = monk,l( seal/c(m0, [m c c ], ς) k, v) and that performs the sealing or the unsealing. We determine 1 j b 1 7→ 2 c for all such e , v = cv k and k k or, this party by picking up the labels from monitors, decorate 1 | | ∈ e1 = send(o, m, v) where key coloring with them, and swapping their position when OwnerLimit(o, m, , , ) = (k, j) or, we go from positive to negative position as we traverse ⊥ ⊥ ⊥ p p p o o o contracts. If a sealing contract is in a positive position, the e1 = class(cv) fi := vi m x . e m x . e and where LocateBarrier{ (cv, m) = (k, j), and m =} owner of the key should be the server of the monitor since mp mo. it is the component that must protect the sealed value from ⊕ its context. Similar constraints apply to the other cases for In addition to eliminating stuck states, the definition of sealing and unsealing contracts. complete monitoring requires that the system blames a com- ponent only when a violation is due to one of the compo- 4.3 The Blame Theorem nent’s unmet obligations. Thus, our definition includes cases Now that we have established complete monitoring for for all immediate checks that the contract system makes and, TFCC, we can prove type soundness for programs that mix furthermore, includes cases that ensure sealing and unseal- typed and untyped code. ing are handled properly. From type soundness for typed code, we know that purely Lemma 2. , is a complete monitor for TFCC. typed components are safe from type errors. In mixed pro- → grams, typed and untyped components interact by passing To prove that , is a complete monitor for TFCC we values through contract monitors. A monitor monu,t(c, e) → j employ subject reduction. The proof combines a progress embeds an untyped value into a typed context because and a preservation lemma which establish that reduction u is the server and t is the client. Similarly, a monitor t,u preserves the subject and does not lead to stuck states: monj (c, eτ ) embeds a typed value into an untyped con- text. Since the boundaries between untyped and typed com- Progress. If ; ; ; l e and S then 0 0 0 ponents are monitored by the contract system, we need the e ,S , eK,SG S or e = v or eK = errorS ∼k or e = 0 0 1 1 0 0 0 contract system to protect values that flow across the bound- blameh ki. → h i j aries as strongly as the type system. For that reason we im- Preservation. If ; ; ; l0 e0, S0 and pose contracts on the interfaces between typed and untyped K ∅ S K0 0 S ∼ 0 e0,S0 , e1,S1 then there exist , such that , components to simulate the types that the typed components h 0i, →0; h ; 0; li e and 0 K 0S S . K ⊆ K expect. Of course, untyped components may not live up to S ⊆ S K ∅ S 0 1 K S ∼ 1 ble parts. We overcome this obstacle and prove preservation K Γ Σ e : τ Well typed mixed terms even in the presence of sealing using the K environment, | | ` which maps keys to row types and we update it upon mixin K Γ Σ e Well formed mixed terms | | ` application. For uses of untyped mixins from typed code, K Γ Σ u cv r Well typed mixed class values we map the newly created key to the row provided with row | | ` ∼ application. In the reverse case we map the new key to the K Γ Σ u o r Well typed mixed objects | | ` ∼ empty row. Recall that after unsealing a class value that contains Figure 15. Relations for Well Typed Mixed Programs a sealed superclass, the sealed methods become reachable again. However the contracts in the class hierarchy do not reflect the types of the now available methods. The last two the expectations that types express, but the contracts catch relations of figure 15 reconstruct these types via a traversal such impedance mismatches. of the class hierarchy that propagates the types of typed In a sound system, since typed code always respects the methods appropriately. type discipline, a typed component should never break any Constructing the types of untyped code is not the only contract used to mediate values between typed and untyped difficulty of proving preservation. In fact, it is even more code. Intuitively, soundness for mixed programs means that crucial to establish that typed and untyped code do not in- the contract system never blames a typed component. termingle in an unprotected manner. Fortunately, complete The first step to formalize soundness is to extend the no- monitoring solves this problem and guarantees that a mon- tion of well-typed terms to include mixed programs. The first itor or a guard always mediates the interaction between a two relations in figure 15 jointly express these roles. The first typed and an untyped component. This insight significantly one type-checks typed components using the contracts-to- reduces the effort of proving preservation as we just have to types correspondence to create a type for embedded untyped ensure that the contract we attach to a migrating typed value components. The second relation traverses untyped compo- corresponds to the value’s type. Put differently, the proof of nents and delegates type-checking of embedded typed com- the blame theorem is reduced to showing that monitors and ponents to the first relation. Again we use the contract on the guards related reductions of well-typed mixed terms result interface between an untyped and a typed component and the in well-typed mixed terms. contracts to types correspondence from figure9 to construct a type to check against the typed code. 5. Related Work Blame Theorem. If e, then e, , ∗ errort,S The body of related work consists of research on combining and e, , ∗ blame∅ |∅t ,S |∅ `. h ∅i 6→ h i h ∅i 6→ h j i typed and untyped languages, polymorphic contracts, and The cornerstone for proving the blame theorem is a types for extensible objects or records. preservation lemma for mixed terms. According to complete monitoring the only source of blame for typed components Gradual typing Gray et al. [16] present a multi-language is a violation of a first-order contract check on a typed value. system that integrates Scheme and Java, using contracts and Due to type soundness for typed code, however, if all first- mirrors to mediate interactions between the two languages. order contracts that guard typed values correspond to the Their system does not handle dynamic class composition type of the values, the checks never fail. Indeed, the preser- because class values cannot flow from Scheme to Java. In vation lemma for mixed programs guarantees that reduction subsequent work, Gray [14] models interoperation between always results in well-typed mixed terms. This implies that Java and Scheme with cross-language inheritance, but does all first-order checks have the desired property. not address dynamic class composition mechanisms such as mixins or first-class classes. Closer to our work, Gray [15] Lemma 3. (Mixed Preservation) If K Σ e , Σ 0 0 0 0 also models interoperation between Java and JavaScript, al- S and e ,S , e ,S then there exist|∅ | K `,Σ such∅ | that` 0 0 0 1 1 1 1 lowing JavaScript code to use Java classes as prototypes. Kh , Σ i →Σ h, K i Σ e and Σ S . K0 ⊆ 1 0 ⊆ 1 0 |∅ | 1 ` 1 ∅ | 1 ` 1 While her model describes dynamic class composition, strin- The proof of mixed preservation demands an extension of gent restrictions are placed on inheritance to make com- the top two relations of figure 15 to intermediate terms. Seal- position safe. In particular, Java classes cannot dispatch to ing and unsealing contracts make this extension challenging. Javascript extensions. As explained, in section3, sealing is a mechanism for hid- Siek and Taha [28] use an object calculus for the for- ing details of a class provided to a component. Unsealing mulation of their gradual typing system for OO program- makes the hidden details available again to the owner of the ming. Their system handles subtyping for objects, but since class after the component returns its result. Thus when we their language contains neither classes nor extensible ob- seal a typed class the contracts on the sealing interface do jects, there is no treatment of inheritance between untyped not fully reflect the type of the sealed class but only its visi- and typed code. Another approach to combining untyped and typed code 6. Status and Outlook is applying type reconstruction to an untyped language. This paper presents the first design for a gradual typing sys- DRuby [12] is such a system for a subset of Ruby. While tem that accommodates class composition across compo- Ruby supports dynamic class composition via a mixin fea- nents with distinct type disciplines. On the typed side, a ture called modules, DRuby’s type system is unable to sup- novel use of row polymorphism allows for specifying in- port the runtime composition of classes that Ruby allows. terface types for mixins, traits, and other manipulations of Bierman et al. [2] formalize the dynamic type that is ] first-class classes. On the untyped side, the introduction of available in C 4.0. The latter’s dynamic types make no pro- sealing contracts allows the system to seal off portions of vision for higher-order data, however. the specialization interface of classes; this sealing ensures the safety of first-class classes as they flow from the typed Runtime sealing Morris [24] proposed sealing in 1973 as world to the untyped one and back. Finally, the paper also a linguistic mechanism to prevent access to private pieces includes a new, easy-to-use proof technique for the proof of of code. More recently, sealing has been used to protect blame theorems in the presence of polymorphism. datatype abstraction [25]. Our work puts us in a position from where we can explore Guha et al. [17] investigate parametricity guarantees in the pragmatics of gradual typing for first-class classes. In the untyped languages via a combination of contracts and seal- near future, we intend to implement this type system and use ing They use so-called coffers—an opaque datatype for it to equip a significant portion of our code base with types. sealing—to wrap arguments to contracted parametric func- tions. Matthews and Ahmed [21] provide a formal validation Acknowledgments Christos Dimoulas and Sam Tobin-Ho- of this approach in a multi-language system combining the chstadt thank Amal Ahmed for several discussions about untyped λ-calculus and , in which sealing is ap- polymorphic contracts and blame. The authors are also plied at language boundaries to ensure parametricity. Ahmed grateful to Stephen Chang, Vincent St-Amour, Aaron Turon, et al. [1] present a finer-grained sealing mechanism that as- and Arjun Guha for comments on early drafts. sociates seals with type abstractions. Our approach extends this line of work by adding partial seals that allow code to References see some aspects of classes rather than blocking out all class [1]A HMED,A.,FINDLER,R.B.,SIEK,J.G., AND WADLER, features. Notice, however, that our proof technique for type P. Blame for all. In Symposium on Principles of Programming soundness differs significantly. Instead of the logical relation Languages (2011), pp. 201–214. of Matthews and Ahmed or the subtyping-based simulation [2]B IERMAN,G.,MEIJER,E., AND TORGERSEN, M. Adding of Ahmed et al., we base our proof on complete monitor- dynamic types to C]. In European Conference on Object- ing. This enables a modular proof structure and the use of Oriented Programming (2010), pp. 76–100. standard subject reduction for establishing type soundness. [3]B ONO, V., PATEL,A., AND SCHMATIKOV, V. A core calcu- lus of classes and mixins. In European Conference on Object- Types for dynamic class composition Row polymorphism Oriented Programming (1999), pp. 43–66. originated as a mechanism to enable for ob- [4]C ARDELLI,L., AND MITCHELL, J. C. Operations on jects. Wand [36] proposes a type inference algorithm for a records. Mathematical Structures in Science 1 simple object-oriented language based on recursive records. (1991), 3–48. These ideas were also adopted into type systems for extensi- [5]D IMOULAS,C.,FINDLER,R.B.,FLANAGAN,C., AND ble records [4, 13, 18]. FELLEISEN, M. Correct blame for contracts: No more scape- Closely related is ML-ART [26], which is capable of en- goating. In Symposium on Principles of Programming Lan- coding first-class classes, but does not provide classes as guages (2011), pp. 215 – 226. a primitive concept. ML-ART does not allow subsumption [6]D IMOULAS,C.,TOBIN-HOCHSTADT,S., AND FELLEISEN, on objects, thus disallowing the familiar polymorphism over M. Complete monitoring for behavioral contracts. In Euro- objects. In the same vein, Fisher [10] presents a calculus pean Symposium on Programming (2012), pp. 211–230. with typed extensible objects using row polymorphism. Her [7]F ELLEISEN,M.,FINDLER,R.B., AND FLATT,M. Seman- calculus also enables an encoding of class-based program- tics Engineering with PLT Redex. MIT Press, 2009. ming, but does not support them as primitive features. Bono [8]F INDLER,R.B.,CLEMENTS,J.,FLANAGAN,C.,FLATT, et al. [3] build on this line of work, and include classes and M.,KRISHNAMURTHI,S.,STECKLER, P., AND FELLEISEN, mixins as primitives in their calculus. Their calculus cov- M. DrScheme: A programming environment for Scheme. ers the use cases of mixins, but their classes do not support Journal of Functional Programming 12, 2 (2002), 159–182. other dynamic uses of first-class classes. None of these mod- [9]F INDLER,R.B., AND FELLEISEN, M. Contracts for higher- els were designed for a gradually-typed setting. order functions. In International Conference on Functional Programming (2002), pp. 48–59. [10]F ISHER,K.S. Type Systems for Object-Oriented Program- ming Languages. PhD thesis, Stanford University, 1996. [11]F LATT,M.,FINDLER,R.B., AND FELLEISEN, M. Scheme [25]P IERCE,B., AND SUMII, E. A bisimulation for dynamic seal- with classes, mixins, and traits. In Asian Symposium on ing. In Symposium on Principles of Programming Languages Programming Languages and Systems (2006), pp. 270–289. (2004), pp. 161–172. [12]F URR,M.,AN,J.-H.D.,FOSTER,J.S., AND HICKS,M. [26]R EMY´ , D. Programming objects with ML-ART an extension Static type inference for Ruby. In Symposium on Applied to ML with abstract and record types. In Theoretical Aspects Computing (2009), pp. 1859–1866. of Computer Software (1994), pp. 321–346. [13]G ASTER,B.R., AND JONES, M. P. A polymorphic type [27]S IEK,J., AND TAHA, W. Gradual typing for functional lan- system for extensible records and variants. Technical Report guages. In Workshop on Scheme and Functional Program- NOTTCS-TR-96-3, University of Nottingham, 1996. ming (2006), pp. 81–92. [14]G RAY, K. E. Safe cross-language inheritance. In European [28]S IEK,J., AND TAHA, W. Gradual typing for objects. In Eu- Conference on Object-Oriented Programming (2008), pp. 52– ropean Conference on Object-Oriented Programming (2007), 75. pp. 2–27. [15]G RAY, K. E. Interoperability in a scripted world: Putting [29]S TRICKLAND, T. S., AND FELLEISEN, M. Contracts for inheritance & prototypes together. In Foundations of Object- first-class classes. In Dynamic Languages Symposium (2010), Oriented Languages (2011). pp. 97–111. [16]G RAY,K.E.,FINDLER,R.B., AND FLATT, M. Fine-grained [30]T AKIKAWA,A.,STRICKLAND, T. S., DIMOULAS,C., interoperability through contracts and mirrors. In Object- TOBIN-HOCHSTADT,S., AND FELLEISEN, M. Gradual typ- Oriented Programming, Systems, Languages, and Applica- ing for first-class classes. Technical Report NU-CCIS-12-02, tions (2005), pp. 231–245. Northeastern University, College of Computer and Informa- tion Science, 2012. [17]G UHA,A.,MATTHEWS,J.,FINDLER,R.B., AND KRISH- NAMURTHI, S. Relationally-parametric polymorphic con- [31]T ANG, A. Perl 6: reconciling the irreconcilable. Symposium tracts. In Dynamic Languages Symposium (2007), pp. 29–40. on Principles of Programming Languages, 2007. [18]H ARPER,R., AND PIERCE, B. A records calculus based [32]T OBIN-HOCHSTADT,S., AND FELLEISEN, M. Interlan- on symmetric concatenation. In Symposium on Principles of guage migration: from scripts to programs. In Dynamic Lan- Programming Languages (1991), pp. 131–142. guages Symposium (2006), pp. 964–974. [19]H ERMAN,D., AND FLANAGAN, C. Status report: Specifying [33]T OBIN-HOCHSTADT,S., AND FELLEISEN, M. The design JavaScript with ML. In ML Workshop (2007). and implementation of Typed Scheme. In Symposium on Principles of Programming Languages (2008), pp. 395–406. [20]L AMPING, J. Typing the specialization interface. In Object- Oriented Programming, Systems, Languages, and Applica- [34]T OBIN-HOCHSTADT,S., AND FELLEISEN, M. Logical types tions (1993), pp. 201–214. for untyped languages. In International Conference on Func- tional Programming (2010), pp. 117–128. [21]M ATTHEWS,J., AND AHMED, A. through run-time sealing, or, theorems for low, low prices! In [35]W AND, M. Complete type inference for simple objects. In European Symposium on Programming (2008), pp. 16–31. Symposium on Logic in (1987). [22]M ATTHEWS,J., AND FINDLER, R. B. The meaning of [36]W AND, M. Type inference for objects with instance variables multi-language programs. In Symposium on Principles of and inheritance. In Theoretical Aspects of Object-Oriented Programming Languages (2007), pp. 3–10. Programming (1994), pp. 97–120. [23]M EJER,E., AND DRAYTON, P. Static typing where possi- [37]W RIGHT,A.K., AND FELLEISEN, M. A syntactic approach ble, dynamic typing when needed: The end of the cold war to type soundness. Information and Control (1994), 38–94. between programming languages. In OOPSLA Workshop on [38]W RIGSTAD, T., NARDELLI, F. Z., LEBRESNE,S., Revival of Dynamic Languages (2004). O¨ STLUND,J., AND VITEK, J. Integrating typed and [24]M ORRIS,JR., J. H. Types are not sets. In Symposium on untyped code in a scripting language. In Symposium on Principles of Programming Languages (1973), pp. 120–124. Principles of Programming Languages (2010), pp. 377–388.