Create Presentation
Download Presentation

Download Presentation
## A Type System for Higher-Order Modules

- - - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - - -

**A Type System forHigher-Order Modules**Derek Dreyer (joint work with Karl Crary and Bob Harper) Fall 2002**SML Generativity**• SML functors are generative: • If result signature has abstract types, each application of the functor generates new types • At the level of type theory (HL), generativity is very easy to enforce: • Restrict types to only be projected from module paths (or, more generally, values)**Higher-Order Functors**signature S = sig type t val x : t end structure SomeX : S = ... functor ApplyToSomeX(F : S -> S) = F(SomeX) functor Id(X : S) = X structure Res1 = Id(SomeX) structure Res2 = ApplyToSomeX(Id) • Res1.t = SomeX.t • Due to generativity of SML functors, Res2.t is a new abstract type!**Applicative Functors**• Would like to express dependency of result of ApplyToSomeX on argument F: ApplyToSomeX : (F : S -> S) -> S where type t = F(SomeX).t • Then, Res2.t = ApplyToSomeX(Id).t =Id(SomeX).t = SomeX.t, as desired.**Applicative Functors**• This is an “applicative” interpretation of functors: • F(X) outputs the same “t” component every time F is applied to X. • Applicativity in O’Caml an easy extension of path restriction (Leroy 95): • Extend paths with named functor applications**Problems with O’Caml**• Say structure Y = X. • According to phase separation,F(Y).t = F(X).t since X and Y have the same type components. • Not so in O’Caml: • Types compared syntactically**Problems with O’Caml**• In the presence of effects, generativity is often critical: signature SYMBOL_TABLE = sig type symbol val string_to_symbol : string -> symbol val symbol_to_string : symbol -> string val eq : symbol * symbol -> bool end**Problems with O’Caml**functor SymbolTableFun() :> SYMBOL_TABLE = type symbol = int (* index into table *) val table = Array.array(initial_size,NONE) ... fun symbol_to_string n = (case Array.sub(table,n) of SOME x => x | NONE => raise (Fail “bad symbol”)) ... end**Importance of Generativity**• Two dynamic instances of SymbolTableFun ought to generate distinct symbol types. • With applicative functors, however, the symbol type provided by any instance of SymbolTableFun will = SymbolTableFun().t**Phase Separation**• Why are applicative functors sensible? • Why is F(X).t well-determined? • Principle of phase separation: • Modules are second-class • Type components of a module can only depend on other type components,not on run-time conditions.**Key Question**• When are two types t1 and t2 equivalent? • If t1 = M1.t and t2 = M2.t, the question is: When are M1 and M2 equivalent? • Most liberal answer: Static Equivalence • G` M1@ M2 : s when their type components are equal**Fully Transparent Modules**• Start by ignoring sealing (M :>s). • Relax Harper-Lillibridge to allow projecting types from any module: • F(X).t no big deal ) functors are applicative • I.e. all modules are determinate**Abstraction**• Now we add opaque sealing: M ::s • Problem: Say A = M ::s and B = M ::s. • A.t and B.t are distinct abstract types. • But by reflexivity of equivalence,A.t = (M ::s).t = (M ::s).t = B.t • So sealed modules must be indeterminate.**Abstraction as an Effect**• Irreflexivity of sealed modules reminiscent of irreflexivity in languages with effects: • Call a module pure if it is free of sealing,and impure otherwise. • Grant M certain privileges only if it is pure: • Can project types from M • Can compare M for equivalence**Abstraction is not Generativity**• Functors still behave applicatively: • F(X).t is perfectly valid, assuming F and X are module variables, since variables are pure. • Sealing allowed in applicative functors • Semantics similar to O’Caml • If we want generativity, we need to distinguish it from abstraction.**Generativity**• Generativity = generation of abstract types at run-time • Violation of phase separation! • Not really, but we’ll pretend... • Track generativity like a “real” effect • Abstract types tied (notionally) to run-time state of module defining them**Static and Dynamic Effects**• Abstraction (from before) is a static effect • Generativity is a dynamic effect • Weak sealing (M ::s) is abstract • Induces just a static effect • Strong sealing (M :>s) is generative • Induces a static and a dynamic effect**Generative Functor Signatures**• Need to know from a functor’s signature whether its body is impure (generative): • Distinguish total vs. partial functor signatures, i.e. applicative (as before) vs. generative • To deserve an applicative signature, the functor’s body must be dynamically pure.**Type System Overview**• Translucency modeled by singletons: [T] ¼ “sig type t end” S(M) ¼ “sig type t = M.t end” • Static equivalence defined almost exactly the same as for singleton kind calculus • Because pure module language very close to type constructor language**Type System Overview**• Module typing judgment: G`k M : s, where k is purity level drawn from lattice • Mostly standard dependent typing rules**S Typing Rules**• M must be pure in p2M in order for the signature s’’[p1M/s] to be well-formed**P Typing Rules**• M must be pure in F(M) in order for the signature s’’[M/s] to be well-formed**Let and Subsumption**• Let has signature annotation, necessary to dodge the avoidance problem: • Subsumption can give a module a weaker signature or purity level:**Base Equivalence Rules**• Equivalence of atomic type modules = equivalence of the types they contain • Need these rules to observe equivalences like Typ [t] ´t:**Unitary Equivalence**• Call a signature unitary if it is of the form: • Modules of unitary signature have no type components, so equivalence is trivial:**Decidability**• Principal signature synthesis • G`k M )s. • Algorithm similar to principal kind synthesis, also synthesizes minimal purity level • Type system is decidable • As with kinds, boils down to decidability of type/module equivalence • Proof nearly identical to Stone-Harper**Existential Signatures**• Say we want un-annotated lets, and the ability to write F(M) and p2M, where M is impure... • Introduce existential signature 9 s:s1.s2.**Elaboration**• Not so easy to introduce existentials... • Use elaboration (same as Harper-Stone): • Existential signatures model hidden modules • 9 s:s1.s2 is really S s:s1.s2. • But when you see an existential, immediately project out the second component... • 9 s:s1.s2 is like [1Bs:s1.2*:s2] in HS**Modules as First-Class Values**• Existential types are a known way of encoding modules as first-class values • Encodable in the system via Church encoding • If s phase-splits to [a:k.t], then ¼9a:kappa.t.**A Primitive Extension**• But we can get open-scope unpacking with a primitive extension:**Importance of Generativity**• Dynamic effects are critical here • Types can now actually depend on run-time conditions • Example: • F must be generative, or else X1@ X2.**Related Work: Russo’s Thesis**• Defines a higher-order module language with only applicative functors • Existential signatures used in a way very similar to Shao’s • Same power as DCH before the addition of generativity and dynamic effects**Related Work: Moscow ML**• Moscow ML combines Russo’s higher-order modules with Standard ML • Allowed to write anything in the body of an applicative functor • Including generative functor applications • Generativity doesn’t work • Can eta-expand a generative functor into an applicative one • Moreover, language is unsound**Related Work: Shao**• Shao has both applicative and generative • Applicative = Transparent • Generative = Opaque • Precludes one from having opaque substructures in an applicative functor • But this is useful, e.g. for datatype’s • Shao can be encoded as a subsystem of DCH that lacks weak sealing**Conclusion**• Static equivalence (HMM-style) is as liberal as possible for second-class • Restrict type equivalence by treating abstraction and generativity as effects: • Abstraction is a static effect • Generativity is a dynamic effect, captured by (generative) functors • Scales to handle real run-time type generation in first-class modules