The RPC Calculus Symmetrical RPC in an Asymmetrical World Ezra Cooper Philip Wadler September 8, 2009
Dream of unified web programming
Dream of unified web programming What we want
Reality of web programming
Reality of web programming It’s a bit more fiddly.
Reality of web programming It’s a bit more fiddly. Here’s why:
Traditional program
Traditional program
Traditional web program .
Traditional web program .
Unified program
Unified program Simplifies the work of web programming
Unified program Simplifies the work of web programming The Links language: http://groups.inf.ed.ac.uk/links
Dream of a unified language Waking up: ◮ Desire to control location explicitly, with a light touch; ◮ Need control for performance and security reasons; ◮ Tricky because of asymmetrical client/server relationship.
Roadblock: Asymmetrical client/server relationship
Roadblock: Asymmetrical client/server relationship
Stateless server Web applications should not store control state at the server.
Stateless server Web applications should not store control state at the server. Server should encode all state and give it to client.
Stateless server Web applications should not store control state at the server. Server should encode all state and give it to client. For this talk, state = call stack.
Example program fun checkPassword ( name , password ) { # load this user’s row from database & check password var u = lookupUser ( name ); u . password == password } fun validate () { var auth = checkPassword ( fieldValue (”name”) , fieldValue (”password”))); if ( auth ) displaySecretDocument (); else displayErrorMessage (); }
Example located program fun checkPassword ( name , password ) server { # load this user’s row from database & check password var u = lookupUser ( name ); u . password == password } fun validate () client { var auth = checkPassword ( fieldValue (”name”) , fieldValue (”password”))); if ( auth ) displaySecretDocument (); else displayErrorMessage (); }
Example located program: server push fun findFlights ( flightQuery ) server { # Query each vendor for its own matching flights for ( vendor ← airlines ()) { var flights = queryVendor ( vendor , flightQuery ); # Send this vendor’s flights to the browser displayFlights ( flights ); } } fun displayFlights ( flights ) client { # Add each flight to the page for ( flight ← flights ) addToPage ( flight ); }
Example: higher-order functions How should this code behave? fun usernameMap ( f ) server { var users = getUsersFromDatabase (); for ( u ← users ) [ f ( u . name) ] } fun userNameFirstThree () client { usersMap ( fun ( name ) { take (3 , name ) } ); }
Example: higher-order functions How should this code behave? fun usernameMap ( f ) server { var users = getUsersFromDatabase (); for ( u ← users ) [ f ( u . name) ] } fun userNameFirstThree () client { usersMap ( fun ( name ) { take (3 , name ) } ); } ⊲ Functions in lexical client-context execute on client.
What I want to show you ◮ How to compile this language for the asymmetrical client-server model,
What I want to show you ◮ How to compile this language for the asymmetrical client-server model, ◮ How the compilation factors into standard techniques,
What I want to show you ◮ How to compile this language for the asymmetrical client-server model, ◮ How the compilation factors into standard techniques, ◮ How these these techniques can be presented formally and concisely.
How it’s done main f g Client Server Call to f (server) {Call f} Call to g (client) {Call g, k} {Continue r, k} Return r from g {Return s} Return s from f Source language: Implementation: call/return style request/response style
Getting technical
Source language: the located lambda calculus
Source language: the located lambda calculus LM | λ a x . N | λ x . N | x | c L , M , N ::= a , b ::= c | s
Source language: the located lambda calculus LM | λ a x . N | λ x . N | x | c L , M , N ::= a , b ::= c | s We eliminate un-located forms λ x . N by explicitly copying the location of their lexical context. So λ c x . L ( λ y . N ) becomes λ c x . L ( λ c y . N )
Source language: the located lambda calculus LM | λ a x . N | x | c L , M , N ::= a , b ::= c | s We eliminate un-located forms λ x . N by explicitly copying the location of their lexical context. So λ a x . L ( λ y . N ) becomes λ c x . L ( λ c y . N )
Semantics Read M ⇓ a V as ” M evaluates, starting at a , to V .” V ⇓ a V ( Value ) L ⇓ a λ b x . N M ⇓ a W N { W / x } ⇓ b V ( Beta ) LM ⇓ a V L ⇓ a c M ⇓ a W δ a ( c , W ) ⇓ a V ( Delta ) LM ⇓ a V
Translation to a client-server system Three techniques: ◮ CPS translation: reifies the control state ◮ Defunctionalization: turns higher-order functions into data (serializable) ◮ Trampolining: inverts control, so state resides at client.
CPS translation (due to Fischer, 1972, via Sabry and Wadler, 1997) Source: L , M , N ::= LM | V V ::= λ x . N | x CPS translation: ( LM ) † K L † ( λ f . M † ( λ x . fxK )) = V † K KV ◦ = ( λ x . N ) ◦ λ x .λ k . N † k = x ◦ = x
Defunctionalization
Defunctionalization target D ::= letrec D and · · · and D ::= f ( � x ) = case x of A D A ::= a set of A items F ( � A ::= c ) ⇒ M f ( � M ) | F ( � M ::= M ) | x | c
Defunctionalization target D ::= letrec D and · · · and D ::= f ( � x ) = case x of A D A ::= a set of A items F ( � A ::= c ) ⇒ M f ( � M ) | F ( � M ::= M ) | x | c function names f , g constructor names F , G
Defunctionalization (orig. Reynolds, 1972) ] fun ∗ ] top [ [ M ] = letrec apply ( fun , arg ) = case fun of [ [ M ] in [ [ M ] ] ] fun � λ x . N � ( � [ [ λ x . N ] = y ) ⇒ [ [ N ] ] { arg / x } where � y = fv ( λ x . N ) [ [ LM ] ] = apply ([ [ L ] ] , [ [ M ] ]) V ◦ [ [ V ] ] = ( λ x . N ) ◦ = � λ x . N � ( � y ) where � y = fv ( λ x . N ) x ◦ = x The operation � M � gives an opaque identifier for the term M .
Trampolining (due to Ganz, Friedman and Wand) ◮ Continually returns control to a top-level trampoline ; ◮ Works on any tail-form program, including CPS programs; ◮ Choice of the trampoline modifies the behavior.
Trampolining ( LM ) T Bounce ( λ z . L t M t ) = (where z is a dummy) V T Return ( V t ) = ( λ x . N ) t λ x . N T = x t = x Behavior depends on our choice of tramp .
Example trampolines Trivial trampoline: tramp ( x ) = case x of Bounce ( thunk ) ⇒ tramp ( thunk ()) | Return ( x ) ⇒ x
Example trampolines Trivial trampoline: tramp ( x ) = case x of Bounce ( thunk ) ⇒ tramp ( thunk ()) | Return ( x ) ⇒ x Step-counting trampoline: tramp ( n , x ) = case x of Bounce ( thunk ) ⇒ print ( n ); tramp ( n + 1 , thunk ()) | Return ( x ) ⇒ x
Our trampoline Since the control state is reified, tramp can split the computation into a client- and a server-side piece. tramp ( x ) = case x of | Bounce ( f , x , k ) ⇒ tramp ( req cont ( k , apply ( f , x ))) | Return ( x ) ⇒ x (This shouldn’t make sense yet; don’t worry.)
Our trampoline Since the control state is reified, tramp can split the computation into a client and a server-side piece. tramp ( x ) = case x of | Call ( f , x , k ) ⇒ tramp ( req cont ( k , apply ( f , x ))) | Return ( x ) ⇒ x (This shouldn’t make sense yet; don’t worry.)
The Big Transformation
First, the target: first-order client-server calculus
The client-server calculus Syntax configurations K ::= ( M ; · ) | ( E ; M ) x | c | F ( � M ) | f ( � M ) | req f ( � terms L , M , N ::= M ) definition set D , C , S ::= letrec D and · · · and D function definitions ::= f ( � x ) = case M of A D alternative sets A a set of A items case alternatives ::= F ( � x ) ⇒ M A function names f , g constructor names F , G
Configurations of the machine
Semantics Client: ( E [ f ( � ( E [ M { � V )]; · ) − → C , S V /� x } ]; · ) if ( f ( � x ) = M ) ∈ C ( E [ case ( F ( � ( E [ M { � V )) of A ]; · ) − → C , S V /� x } ]; · ) if ( F ( � x ) ⇒ M ) ∈ A Server: ( E ; E ′ [ f ( � ( E ; E ′ [ M { � V )]) − → C , S V /� x } ]) if ( f ( � x ) = M ) ∈ S ( E ; E ′ [ case ( F ( � ( E ; E ′ [ M { � V )) of A ]) − → C , S V /� x } ]) if ( F ( � x ) ⇒ M ) ∈ A Communication: ( E [ req f ( � ( E ; f ( � V )]; · ) − → C , S V )) ( E ; V ) − → C , S ( E [ V ]; · )
Now, the translation
Transformation on terms ( λ a x . N ) ◦ � λ a x . N � ( � y = fv ( λ a x . N ) = y ) � x ◦ = x c ◦ = c V ∗ V ◦ = ( LM ) ∗ apply ( L ∗ , M ∗ ) = V † [ ] cont ([ ] , V ◦ ) = ( LM ) † [ ] L † ( � M � ( � where � = y , [ ])) y = fv ( M )
Recommend
More recommend