
Type Inference for First-Class Messages with Match-Functions Paritosh Shroff Scott F. Smith Johns Hopkins University {pari, scott}@cs.jhu.edu Abstract takes an abstract first-class message m as a argument and forwards it to o. We can create a Ftp proxy server as: Messages that can be treated as first-class entities are called first- class messages. We present a sound unification-based type infer- let FtpProxy = ProxyServer ← new(ftp) ence system for first-class messages. The main contribution of the where ftp is a Ftp object. A typical use of this new proxy is paper is the introduction of an extended form of function called a match-function to type first-class messages. Match-functions can FtpProxy ← send(get(0paper.ps.gz0)) be given simple dependent types: the return type depends on the type of the argument. We encode objects as match-functions and get is a method in ftp. Delegation of abstract messages cannot be messages as polymorphic variants, thus reducing message-passing easily expressed without first-class messages, since the message to simple function application. We feel the resulting system is sig- can be changed at run-time and hence must be abstracted by a vari- nificantly simpler than previous systems for typing first-class mes- able m. [MN00] and [Nis98] further discuss how first-class mes- sages, and may reasonably be added to a language design without sages can exploit locality by abstracting remote computations in overly complicating the type system. distributed object-oriented computing. Modern typed object-oriented programming languages (e.g. Java, Keywords C++) do not support first-class messages. Smalltalk [GR89] and Objective-C [PW91] provide untyped first-class messages. First-class messages, polymorphic variants, object-oriented pro- gramming, constraint-based type inference, unification, object- The static type-checking of first-class messages presents two main encoding. difficulties: 1. Typing first-class messages that can be placed in variables and 1 Introduction passed as values, e.g. m = fmessage(): we need a way to express and type standalone messages. First-class messages are messages that can be treated as first-class values and bound to program variables. Hence, we can write obj ← 2. Typing abstract message-passing to objects, such as o ← x: x, where obj is an object, x a program variable and ← represents since x can be any message in o and these different messages message passing. Since the message assigned to x can be varied can have different return types, o ← x doesn’t have a single at run-time, we can change the message sent to obj dynamically. return type but rather its type depends upon the type of x. We This is the exact dual of dynamic dispatch, where we change the need to be able to encode its static type such that it depends object at run-time—here we want to be able to change the message on the abstract type of x. at runtime. We solve the first problem by encoding messages as polymorphic First-class messages are useful in typing delegate objects, which variants which have standalone types, i.e., a polymorphic variant forward messages to other objects. For example, in ‘fmessage() has type ‘fmessage(). let m = fmessage() in The second problem is more challenging, and the most important let o1 = {forward(x) = o2 ← x} in contribution of this paper is the simplicity of its solution. We in- o1 ← forward(m) troduce an extended form of function (inspired by SML/NJ’s pat- tern matching functions) called a match-function, which always o1 is a delegate object where forward (x) is the method that delegates, i.e. forwards, x to o2. fmessage() is the first-class message which takes a variant as argument and has a return type that can de- gets assigned to m, then x, and finally forwarded to o2. Such del- pend on the argument variant. We call these dependent types egate objects are ubiquitous in distributed systems such as proxy match-types. We encode objects as match-functions, thus reduc- servers giving access to remote services (e.g. ftp) beyond a fire- ing message sending to simple function application. There are sev- wall. The following is an example of a proxy server cited by eral other solutions to typing first-class messages in the literature Muller¨ [MN00]: [Wak93, Nis98, MN00, Pot00]; the main advantage of our approach is its simplicity. let ProxyServer = {new(o) = {send(m) = o ← m}} This creates an object ProxyServer with a method new that receives We organize the rest of the paper as follows. We review OCaml’s an object o and returns a second object with a method send. send polymorphic variants in Section 1.1, and review the duality of records and variants in Section 1.2. In Section 2 we define DV, existing polymorphic variant type systems above. a core language with match-functions. In Section 3 we present a type system for DV that includes match-types, and show it is sound. In Section 4 we present a constraint-based type inference system 1.2 The Duality of Variants and Records for inferring match-types along with an algorithm for simplifying the constrained types to human-readable constraint-free types and It is well-known that variants are duals of records in the same man- prove its soundness. The net result is a type inference algorithm ner as logical “or” is dual to “and”. A variant is this field or that for the original DV type system. Section 5 illustrates all aspects of field or that field . ; a record is this field and that field and that the system with a series of examples. In Section 6 we show how field . Since they are duals, defining a record is related to using a objects with first-class messages can be faithfully encoded with variant, and defining a variant is like using a record. In a program- match-functions. Section 7 discusses the related work. ming analogy of DeMorgan’s Laws, variants can directly encode records and vice-versa. Some portions of the paper are grayed out. They represent optional extensions to our system which enhance its expressiveness at the A variant can be encoded using a record as follows: expense of added complexity. The paper can be read assuming the match s with ‘n1 (x1) → e1 | ... | ‘nm (xm) → em ≡ gray portions don’t exist. We recommend the readers skip these s {n1 = fun x1 → e1,..., nm = fun xm → em} grayed out portions during their initial readings to get a better un- ‘n(e) ≡ (fun x → (fun r → r.n x)) e derstanding of the core system. Similarly, a record can be encoded in terms of variants as follows: 1.1 Review of Polymorphic Variants {l1 = e1,..., lm = em} ≡ fun s → match s with ‘l1 (x) → e1 | ... | ‘lm (x) → em Variant expressions and types are well known as a cornerstone of functional programming languages. For instance in OCaml we may where x is any new variable. The corresponding selection encoding is: declare a type as: e.lk ≡ e ‘lk ( ) type feeling = Love of string | Hate of string | Happy | Depressed where could be any value. Polymorphic variants [Gar98, Gar00], implemented as part of Ob- One interesting aspect about the duality between records and vari- jective Caml [LDG+02], allow inference of variant types, so type ants is that both records and variants can encode objects. Tradition- declarations like the above are not needed: we can directly write ally, objects have been understood by encoding them as records, expressions like ‘Hate("Fred") or ‘Happy. but a variant encoding of objects also is possible: A variant is a message, and an object is a case on the message. In the variant en- We use the Objective Caml [LDG+02] syntax for polymorphic vari- coding, a nice added side-effect is it is easy to pass around messages ants in which each variant name is prefixed with ‘. For example in as first-class entities. OCaml 3.07 we have, The problems with the above encodings, however, is neither is com- let v = ‘Hate ("Fred");; plete in the context of the type systems commonly used for records val v : [> ‘Hate of string] = ‘Hate "Fred" and variants: for example, if an ML variant is used to encode ob- [> ‘Hate of string] is the type of the polymorphic variant jects, all the “methods” (cases of the match) must return the same ‘Hate ("Fred"). The “>” at the left means the type is read as type! This is why objects are usually encoded as records. But if the “these cases or more’’. The “or more” part means these types are variant encoding could be made to work, it would give first-class polymorphic, it can match with a type of more cases. status to messages, something not possible in the record system. Correspondingly, pattern matching is also given a partially specified In this paper we introduce match-functions, which are essentially type. For example, ML-style pattern match functions, but match-functions in addi- tion support different return types for different argument types via let f = fun ‘Love s -> s | ‘Hate s -> s match-types. A match-function-encoding of objects is as power- -: val f : [< ‘Love of string | ‘Hate of string] -> string ful as a record encoding, but with additional advantage of allowing [< ‘Love of string | ‘Hate of string] is the inferred vari- first-class messages to typecheck. ant type. The “<” at the left means the type can be read “these cases or less”, and since ‘Hate ("Fred") has type 2 The DV Language [> ‘Hate of string], f ‘Hate ("Fred") will typecheck.
Details
-
File Typepdf
-
Upload Time-
-
Content LanguagesEnglish
-
Upload UserAnonymous/Not logged-in
-
File Pages15 Page
-
File Size-