read/web

How Many Continuations can Dance
on the Head of a Pin
Matthias Felleisen
King of Continuations
and
Professor of Computer Science
What does this mean?
Academia and the Real World
• The theory of continuations (85-95)
• Phil: “Here is the king of continuations.”
• The practice of PLT Scheme at ICFP (95-…
• Dick Gabriel: “Theoreticians just count how
many continuations …”
• It’s really all about the Web.
What does this mean?
Common Lisp and PLT Scheme
• Functions are values
like all other values.
• Continuations are fine
values, too.
• Tail calls are jumps.
• Macros don’t surprise
you.
• Functions live on a
different planet.
• What’s a continuation?
• We have do.
• Who needs protection?
It’s really about the Web.
What does this mean?
• The “Orbitz” Problem
• Programming the Interactive Web
• Continuations, closures and the Web
• Academia and the Real World, Again
The Interactive Web and the “Orbitz Problem”
The “Orbitz Problem”
The same problem found on Web sites of
• Microsoft / Apple / …
• Continental Airlines / Orbitz / Hertz / …
• the National Science Foundation …
• … and many other companies
Programming the Interactive Web
The Interactive Web
Numerous Web APIs:
• The Common Gateway Interface (CGI)
• Java Servlets
• Active Server Pages, Java Server Pages
• Scripting languages (Perl, PHP, etc)
• Microsoft’s Web Services
Printing a Message (Console)
(print “Hello, World\n”)
(exit)
Printing a Message (Web)
(print
<html>
<head><title>Test</title>
</head>
<body>
<p>Hello, World!</p>
</body>
</html> )
(exit)
Do it! .. In DrScheme
Printing Uptime (Console)
(print “Uptime: %s\n”
(system “uptime”))
(exit)
Printing Uptime (Web)
(print
<html>
<head><title>Uptime</title>
</head>
<body>
<p>(system “uptime”)</p>
</body>
</html>)
(exit)
Do it! … In DrScheme …
Area of Circle (Console)
(define r (read “Enter radius:” )
(print “area is %d\n”
(* 3.14 r r))
(exit)
Area of Circle (Web)
Enter
(define r
(get_binding
“radius”
bindings))
(print
<p>area is
(3.14*r*r)</p>
Adding Two Numbers (Console)
(define n1
(prompt-read “Enter first:”))
(define n2
(prompt-read “Enter second:”))
(print “sum: %d\n”(+ n1 n2))
(exit)
Adding Two Numbers: User Interfaces
Enter first:
Enter second:
Enter first:
Enter second:
Adding Two Numbers: Web Interactions
Adding Two Numbers: Web Interactions
Adding Two Numbers: Web Interactions
Adding Two Numbers: Web Interactions
Adding Two Numbers: Web Interactions
Adding Two Numbers: Web Interactions
Adding Two Numbers (Web)
Enter first:
(define n1
(get “n1 bindings))
<form>…</form>
Adding Two Numbers: The Problem
• Web scripts write a page, then
terminate
• When the user replies, another script
reads the form’s bindings and performs
the next step
Adding Two Numbers
(Web)
Enter first:
(define n2
(get_binding
“n2”
bindings))
<p>sum:
(+ n1 n2)</p>
(define n1
(get_binding
“n1”
bindings))
<form>…</form>
Enter second:
In Practice
• System halts cleanly with an error
– The user doesn’t get a useful answer
– The user may not understand the error
– User expended a lot of effort and time
• Program captures variable by accident
(i.e., it implements dynamic scope!), or
• “internal server error”
Adding Two Numbers
(Web)
Enter first:
(define n1
(get_binding “n1”
bindings))
(define n2
(get_binding “n2”
bindings))
<p>sum:
(+ n1 n2)</p>
(define n1
(get_binding
“n1”
bindings))
<form>…</form>
Enter second:
n1:
The Actual Form
<html>
<head>
<title>The Addition Page</title>
<body>
<p>Enter the second number:</p>
<form method="get"
action="http://www. .../cgi-second.ss">
<input type="hidden" name=“n1" value=“1729">
<input type="text" name=“n2" value="0">
</form>
</html>
General Web Programming Problems
• “Invert” the program
• Manually tracks hidden fields
• It’s difficult and mistakes have painful
consequences.
Bad News
That’s the easy part!
The Real Picture
The script
and the user
are
coroutines!
Event lines
script
user
Control Flow: Back Button
script
user
script
user
A silent action!
Control Flow: Cloning
script
user
script
user
Control Flow: Bookmarks
script
user
script
user
What Programmers Need
“Multiply-resumable and
restartable coroutines”
• No language has exactly this – the new
control operator for the Web
• How do we implement it?
How to Reengineer Programs for the Web
Automated Software Engineering 2002
J. Automated Software Engineering 2003
The Legacy Code
(define n1
(read “Enter first: ”))
(define n2
(read “Enter second: ”))
(print “sum: %d\n”
(+ n1 n2))
(exit)
How we should take this code to the Web
(define n1
(read
“Enter first: ”))
(define n2
(read
“Enter second: ”))
(print
“sum: %d\n”
(+ n1 n2)
(exit)
(define n1
(read/web
<form>Enter first: </form>))
(define n2
(read/web
<form>Enter second: </form>))
(print
<p>sum:
(+ n1 n2)</p>)
(exit)
But: the program structure is destroyed
(define n1
(read/web
<form>Enter first: </form>))
(define n2
(read/web
<form>Enter second: </form>))
(print
<p>sum: (+ n1 n2)</p> ))
(exit)
(define (main)
(print
<form action=“f1”>
Enter first:
<input name=“n1”>
</form> ))
(define (f1 form)
(print
<form action=“f2”>
<input hidden name=“n1”
value= (form-n1 form)>
Enter second:
<input name=“n2”>
</form>))
(define (f2 form)
(print
The sum is
(+ (form-n1 form) (form-n2 form))
The Reengineering Challenge
• Web interfaces have grown up:
from “scripts” to “programs”
(or “services”)
• Need debugging, maintenance, evolution, …
• We would like a “Web compiler” that automatically
– splits programs into procedures by form
– propagates fields as needed
– preserves behavior yet accommodates “bizarre” control
The Key Insight
The manual conversion
simply implements the
continuation-passing style
transformation!
Step 1: Create Function for the “Rest of the
Computation”
(define n1
(read/web <form>Enter first: </form>))
… n2 …
(read/web/k
<form>Enter first: </form>
(lambda(n1)
… n2 …
The Result
(read/web/k
<form>Enter first: </form>
(lambda (n1)
(read/web/k
<form>Enter second: </form>
(lambda(n2)
(print <p>sum:
(+ n1 n2)</p>)
Lift Functions
(define (main)
(read/web/k
<form>Enter first: </form> f1))
(define (f1 n1)
(read/web/k
<form>Enter second: </form> f2))
(define (f2 n2)
(print <p>sum:
(+ n1 n2)</p>
Step 2: Propagate Free Variables
(define (main)
(read/web/k
<form>Enter first: </form> f1))
(define (f1 n1)
(read/web/k/args n1
<form>Enter second: </form> f2))
(define (f2 n1 n2) =
(print <p>sum:
(+ n1 n2)</p>)
Convert to Web API
(define (f1 n1)
(read/web/k/args n1
<form>Enter second: </form> f2))
(define (f1 form)
(print
<form action=“f2”>
<input hidden name=“n1”
value=(form-n1 form)>
Enter second:
<input name=“n2”>
</form>))
Resulting Web Application
(define (main)
(print
<form action=“f1”>
Enter first:
<input name=“n1”>
</form>))
(define (f1 form)
(print
<form action=“f2”>
<input hidden name=“n1”
value=(form-n1 form)>
Enter second:
<input name=“n2”>
</form>))
(define (f2 form)
(print
<p>sum: (+ (form-n1 form) (form-n2 form))</p>))
Summary
Three transformations:
• Make all value receivers explicit functions
• Make all functions top-level, replace free
variables with explicit parameters
• Replace first-class functions with first-order
representations
A Remarkable Coincidence
These are known as:
• Continuation-passing style transformation
• Lambda lifting
• Closure conversion (to first-order values)
You’ve studied them in academic compilers &
languages courses, especially when functions are
ordinary values.
Can’t Languages do Better?
European Symposium on Programming 2001
J. Higher-Order Symbolic Logic 2004
see also:
Hughes (1999), Queinnec (ICFP 2000),
Thielman (ESOP 2002)
Program Structure Reinstatement
What we have:
(define n1
(read
“Enter first: ”))
(define n2
(read
“Enter second: ”))
(print
“sum: %d\n”
(+ n1 n2))
(exit)
What we want:
(define n1
(read/web
<form>Enter first:</form>))
(define n2
(read/web
<form>Enter second:</form>))
(print
<p>sum:
(+ n1 n2)</p>
(exit)
Now What?
APIs offer form, cookie, &c primitives
Why not an API with read/web ?
Now what?
Programmers:
Stand up for your rights –
make server implementers work harder!
The Real Primitive
read/web is a small lie:
(define n1
(read/web
<form action=???>Enter first:</form>))
We provide send/suspend:
send/suspend generates the
URL that resumes computation
(define n1
(send/suspend
(lambda (k)
<form action=k>Enter first:</form>))
Generated URLs
send/suspend generates a URL:
http://host/servlets/add.ss;id281*k2-95799725
When a consumer uses this URL, the Web
server delivers the filled out form as the result
of the call to send/suspend .
And send/suspend is what …
… a plain old call-with-current-contiuation.
The PLT Web Server
• send/suspend associates url’s with
continuation objects
• reading and writing becomes a
coroutine action
• the rest is (interesting) engineering
The Moral of the Story
How Many Continuations can Dance on the Head of a Pin
933262154439441526816992388
562667004907159682643816214
685929638952175999932299156
089414639761565182862536979
208272237582511852109168640
00000000000000000000000
So what does this mean?
Because we have closures and
continuations in Scheme and
because this motivates us to
think about CPS and friends,
we can design, implement, and
teach robust interactive Web
programs easily.
Can Common LISPers do it?
Paul Graham did it.
But with closures and
hygienic macros, things
would have been so much
easier and cleaner.
The End
http://www.plt-scheme.org/
Credits …
•
•
•
•
Shriram Krisnamurthi (Brown)
Robert Bruce Findler (Chicago)
Matthew Flatt (Utah)
Paul T. Graunke (Northeastern)