Hiding local state in direct style: a higher-order anti-frame rule Franc ¸ois Pottier June 17th, 2008 1 / 67
In a nutshell Several type systems and logics keep precise track of how mutable state is allocated, affected, de-allocated, or aliased. The upside of this idea is that this can help prove properties of programs. The downside is that this can betray the use of mutable state in functions whose behavior is pure... 2 / 67
In a nutshell A function that allocates a fresh piece of state, works with it, and de-allocates it prior to returning (` a la runST) can be given a pure specification in these type systems and logics. A function whose state persists across invocations cannot . 3 / 67
Contents Introduction Basics of the type system A higher-order anti-frame rule Applications Conclusion Bibliography 4 / 67
Hidden state Many “objects” (or “modules”, “components”, “functions”) rely on a piece of modifiable internal state, which persists across invocations , yet they publish an informal specification that does not reveal the existence of such a state. 5 / 67
Hidden state For instance, a memory manager might maintain a linked list of freed memory blocks. Yet, clients need not, and wish not, know anything about it. They need not even be told that the memory manager has a certain abstract invariant. Telling them so would force them to publish the fact that they require and preserve this invariant. In short, every (direct or indirect) client of the memory manager would have to declare itself as such! That would not be modular. 6 / 67
Hiding versus abstraction Hiding is not abstraction. Hiding pretends that there is no internal state, while abstraction acknowledges that there is one, but makes its type (and properties) abstract. Both protect the internal state from interference by clients, and protect clients from changes in the representation of the internal state. 7 / 67
Hiding versus abstraction Hiding offers the additional advantage that objects with internal state appear as ordinary objects, hence can be untracked. It is not necessary to ask how they are aliased , who owns them, or how they internal state is threaded through client computations. Abstraction offers the additional advantage that clients can reason about state changes. The computational state, which has abstract type, can be declared to represent some logical state, at a concrete type. For instance, the internal state of a hash table can be declared to represent a mathematical finite map. In practice, both hiding and abstraction are useful, albeit in different circumstances. 8 / 67
Hiding versus abstraction Consider an object that produces a stream of the prime numbers. If it is specified that each invocation returns the next prime number, then the internal state can only be abstract. If it is only specified that each invocation returns some prime number, then the state can be hidden. 9 / 67
Hiding versus abstraction Whether an object’s internal state can be hidden depends not on the object’s actual behavior, but only on its specification. As specifications become weaker, opportunities for hiding state increase! When specifications are just types, which describe the structure of data and the structure of the store, these opportunities are quite numerous. 10 / 67
Towards a formalization How could the concept of hidden state be made precise in a formal framework for reasoning about programs? In this talk, I attempt to provide an answer... 11 / 67
Towards a formalization Which formal frameworks provide an appropriate setting in which to ask (and answer) this question? Any system that keeps track of aliasing and ownership properties, and allows expressing pre- and post-conditions that describe the structure of the store, should do. Pick one of: Hoare logic / separation logic / bunched typing / type systems with regions and capabilities / Hoare type theory / you-name-it... 12 / 67
Towards a formalization In this talk, I use the vocabulary of a type system for an ML-like programming language [Chargu´ eraud and Pottier, 2008]. It should be possible to transpose the main idea to another setting. (If you think I should do so, please do come and talk to me!) 13 / 67
Contents Introduction Basics of the type system A higher-order anti-frame rule Applications Conclusion Bibliography 14 / 67
The host type system This type system is the setting in which I develop a rule for hiding state and prove (syntactic) type soundness. The details of the type system are somewhat unimportant for this talk, so I will flash them by... 15 / 67
Regions A region σ is a static name for a set of values. The type [ σ ] is the type of the values that inhabit the region σ . In this talk, there are only singleton regions, so a region σ is a static name for a value, and [ σ ] is a singleton type. 16 / 67
Capabilities A singleton capability { σ : θ } is a static token that serves two roles. First, it carries a memory type θ , which describes the structure and extent of the memory area to which the value σ gives access. Second, it represents ownership of this area. For instance, { σ : ref int } asserts that the value σ is the address of a reference cell, and asserts ownership of this cell. Capabilities are linear: they are never duplicated. 17 / 67
Capabilities (summary) On top of singleton capabilities, one builds composite capabilities: ∅ C ::= empty heap | { σ : θ } singleton heap | C 1 ∗ C 2 (separating) conjunction | ∃ σ.C embedded region | C 1 ⊗ C 2 (explained later on) There is a clear analogy between capabilities and separation logic assertions. 18 / 67
Memory types Here is a summary of memory types: ⊥ | unit | θ 1 + θ 2 | θ 1 × θ 2 θ ::= data | χ 1 → χ 2 functions | [ σ ] indirection via a region | ref θ reference cell | θ ∗ C embedded capability | ∃ σ.θ embedded region | θ ⊗ C (explained later on) Memory types express ownership, so they are linear. 19 / 67
Value types (summary) Values receive value types: ⊥ | unit | τ 1 + τ 2 | τ 1 × τ 2 τ ::= data | χ 1 → χ 2 functions | [ σ ] indirection via a region | τ ⊗ C (explained later on) Values are non-linear: they can be discarded or duplicated at will. Value types form a subset of memory types, deprived of references and embedded capabilities. 20 / 67
Judgements about values Judgements about values take the form: ∆ ⊢ v : τ Duplicable type environments ∆ associate value types with variables. Values do not involve computation, which is why this judgement form does not involve any capabilities, either as input or as output. 21 / 67
Judgements about terms Judgements about terms take the form: Γ � t : χ The type environment Γ and the computation type χ respectively describe the initial and final shapes of the store. This is analogous to a Hoare triple in separation logic. A computation type is a value type, plus regions and capabilities. A type environment associates computation types with variables, and contains capabilities. τ | χ ∗ C | ∃ σ.χ | χ ⊗ C χ ::= ∅ | Γ , x : χ | Γ , C | Γ ⊗ C Γ ::= 22 / 67
Typing rules for references References are tracked: allocation produces a singleton capability, which is later required for access. ref : τ → ∃ σ. { σ : ref τ } [ σ ] { σ : ref τ } [ σ ] → { σ : ref τ } τ get : set : { σ : ref τ 1 } ([ σ ] × τ 2 ) → { σ : ref τ 2 } unit 23 / 67
Contents Introduction Basics of the type system A higher-order anti-frame rule Applications Conclusion Bibliography 24 / 67
The first-order frame rule The first-order frame rule states that, if a term behaves correctly in a certain store, then it also behaves correctly in a larger store, and does not affect the part of the store that it does not know about: Γ � t : χ Γ , C � t : χ ∗ C This rule can also take the form of a simple subtyping axiom: χ 1 → χ 2 ≤ ( C ∗ χ 1 ) → ( C ∗ χ 2 ) 25 / 67
The first-order frame rule The frame rule makes a capability unknown to a term , while known to its context. To hide a piece of local state is the exact dual: to make a capability known to a term, yet unknown to its context. 26 / 67
Hidden state via frame rules In a programming language with higher-order functions, one could hope to be able to exploit the duality between terms and contexts. By viewing the context as a term, a continuation, one could perhaps use a frame rule to hide a piece of local state. This is the approach of Birkedal, Torp-Smith, and Yang [2006], who follow up on earlier work by O’Hearn, Yang, and Reynolds [2004]. 27 / 67
Hidden state via frame rules Imagine that we have a provider, a term of type: ((unit ∗ C ) → (int ∗ C )) ∗ C The provider initially establishes C and returns a function that requires C and preserves it. This could be the type of a stream of integers, with internal state. 28 / 67
Hidden state via frame rules We now wish to hide C and pretend that the provider is an ordinary function, of type unit → int. Applying the frame rule to the provider would not help. We must apply the frame rule to the client. 29 / 67
Recommend
More recommend