• Expression e can only do what it has capabilities for • If e is given no capability to do X, it cannot do X! • Can be done statically, or dynamically • Statically, object capabilities correspond to reference capabilities. • An alternative static approach to restricting effects is an effect system. • Both are useful for the same purpose – so why do we have both?
know them! • But nobody talks about them • We’ll talk about trade-offs between specific systems for the same problem • Outsiders / newcomers have to learn it by trial and error • No established language to talk about these things • Let’s talk
generalizable • I’ve worked on reference capability systems and effect systems, sometimes separate, sometimes together! • This describes most of the research I’ve done • Drawbacks of reference capabilities, and partial work-arounds • Drawbacks of effect systems • How these drawbacks affected the design of three systems: • M# (OOPSLA 2012), JavaUI (ECOOP 2013), SJS (OOPSLA 2016)
to restrict mutation • Can be used for data race freedom • E.g., M# (OOPSLA 2012), a precursor to Pony (AGERE 2015) • M# was the primary programming language for Midori, an attempt to productize the Singularity OS
with x */ y = z; /* more concurrent work with x */ {x:readable T,y:writable T,z:writable T} This cannot be parallelized! But y and z aren’t used in unsafe ways. I call this the Use-Mention Distinction after a similar concept from analytic philosophy.
system, NoHeapAccess ≤ HeapAccess • Г, : , : ⊢ = ∶ 𝑢𝑢𝑢𝑢𝑢𝑢 | • This super-simple effect system could be used to parallelize “x=y” The simplicity of capabilities comes from conflating use and mention: Can’t use what you can’t even mention
not fatal • M# used to write most of Midori (several million LOC) • Pony is used in industry • Arguably Rust has some flavor of this • Framing / Weakening to the rescue! • Refactor the x=y elsewhere, frame away x and y and parallelize • But this only works for some problems
UI thread • Background threads are used for expensive work • All UI updates *must* occur on the UI thread • Interesting reasons; see ECOOP’13 paper for discussion • Don’t do it? Exceptions, crashes, corruptions, and more. Background Thread UI Thread
UI thread requires identifying UI elements statically • References to UI elements == reference capabilities • Idea: Don’t let background threads hold refs to UI objects! • Sound! • Also useless for existing frameworks…
= ...; ...new Thread() { public void run() { // do really slow computation Display.asyncExec(new @UI Runnable() { public void run() { label.setText("Complete!"); } }); } }... Local on UI thread Captured as member of BG thread Captured again Used on UI thread Must distinguish use and mention! UI background UI data flow So we’ll just always use effects, right?
need to indicate variables used transitively for writing • With typical assumptions: Γ ⊢ x. f := e : U |{wr(x)} • What about let x = e1 in e2? • If e2’s effect mentions x, how do we rename? • If e1 ≡ y.f.g, we need to work back towards y • But if e2 reassigns y, or y.f, or y.f.g… we need flow-sensitive points-to analysis • Problem: Naming in the presence of mutation is hard! • Think this gets ugly? I tried to work it out for all of M#. • Think *that* sounds ugly? Wait till you see what else I tried!
of reference immutability get so complicated so quickly? 1. Naming things is hard (+ mutation + aliasing…) 2. The effect system has to work out on its own how variables are used • So why is the capability version simpler? • Type contexts enforce invariants • If x and y have the same type, they are treated the same • Adding capabilities to the types means the capabilities conflate what types already do – resulting in less friction for developers or designers But is it only this “kind” of system that breaks this way?
dynamically add and remove object fields • So JavaScript breaks a fundamental assumption for optimization • This is one of many reasons JITs are used for JavaScript: • Generate code specialized to the layout of one set of objects • If the layout changes, throw away and recompile at runtime • Great performance! • Terrible memory usage! Goal: Using types, remove this pathology, and compile field accesses statically & efficiently
fields are changed in *each* object {x:num, get : ()-->num, inc : ()--[this.x]-->() } • Good: Fixes original and examples with receiver escape! • Bad: need points-to information… solvable • Worse: rep exposure causes abstraction issues. No common supertype of f & g can call inc: f:{x:num, get : ()-->num, inc : ()--[this.x]-->() } g:{y:num, get : ()-->num, inc : ()--[this.y]-->() }
quantify modified field { x:num, …, inc : ∃x.()--[this.x]-->() } • Then account for variable # of fields: { x:num, …, inc : ∃X.()--[wr(this.X)]-->() } • Good: The two inc signatures have a common abstraction! • Bad: Row variable disconnected from field presence… Still can’t call inc!
inappropriately complicated? • Context required type inference • Type inference with records requires row variables • (Mitchell Wand invented rows for this purpose – LICS’87) • Type inference with records and subtyping requires row constraints • Lots of work by Cardelli, Smith, Trifinov, Pottier, and others in the 1990s • Hard to tell at the time – and the impl. still uses these! • In hindsight: Clearly too complex, inference would have had TERRIBLE error messages
splitting reference capability A into B and C: • B and C together permit at most what A allowed • B (resp. C) assumes interference as assumed by A, plus C (resp. B) • Used to enforce invariants (immutability or more) in: • Rely-Guarantee references (PLDI’13, TOPLAS’17) • Rely-Guarantee protocols (ECOOP’14, ECOOP’16) • All reference immutability systems (M#, Pony, Javari, OIGJ…) • Kappa (ECOOP’17) • … Compatibility is an underappreciated aspect of capabilities. We solved the fixed layout problem with compatible concrete & abstracted capabilities
place • Capabilities permit (subjectively) simpler systems for controlling effects, but can’t distinguish use and mention • Effects are more powerful and distinguish use and mention, but data- specific effects can quickly get out hand • Effects can be a useful intermediate stage of designing a capabilities system • Using capabilities to enforce global invariants is underappreciated