Continuations
Transcript of Continuations
Continuations & Web Applications
-Balaji Damodaran, ThoughtWorks
Contiuation
● Continuation is Time Travel
● Continuation is saving the control flow of a program's execution
class ControlCentre
def exploreTerrainmarsPlateau = Plateau.new(5, 5)alpha = Rover.new("alpha")source = Position.new(1, 2, "N")alpha.land(marsPlateau, source)destination = alpha.explore("LMLMLMLMM")print destination
endend
class ControlCentre
def exploreTerrainmarsPlateau = Plateau.new(5, 5)alpha = Rover.new("alpha")source = Position.new(1, 2, "N")alpha.land(marsPlateau, source)destination = alpha.explore("LMLMLMLMM")print destination
endend
class ControlCentre
def exploreTerrainmarsPlateau = Plateau.new(5, 5)alpha = Rover.new("alpha")source = Position.new(1, 2, "N")alpha.land(marsPlateau, source)destination = alpha.explore("LMLMLMLMM")print destination
endend
Control Scope
Execution Context
marsPlateau
alpha
class ControlCentre
def exploreTerrainmarsPlateau = Plateau.new(5, 5)alpha = Rover.new("alpha")source = Position.new(1, 2, "N")alpha.land(marsPlateau, source)destination = alpha.explore("LMLMLMLMM")print destination
endend
Control Scope
Execution Context
marsPlateau
alpha
Current Continuation
source = Position.new(1, 2, "N")alpha.land(marsPlateau, source)destination = alpha.explore("LMLMLMLMM")print destination
class ControlCentre
def exploreTerrainmarsPlateau = Plateau.new(5, 5)alpha = Rover.new("alpha")callcc{|@cc|}callcc{|@cc|}source = Position.new(1, 2, "N")alpha.land(marsPlateau, source)destination = alpha.explore("LMLMLMLMM")print destinationreturn @ccreturn @cc
endend
class ControlCentre
def exploreTerrainmarsPlateau = Plateau.new(5, 5)alpha = Rover.new("alpha")callcc{|@cc|}callcc{|@cc|}source = Position.new(1, 2, "N")alpha.land(marsPlateau, source)destination = alpha.explore("LMLMLMLMM")print destinationreturn @ccreturn @cc
endend
> c = exploreTerrain“1 3 N”=> #<Continuation:0xb744a8c4>
> c.call“1 3 N”=> #<Continuation:0xb744a8c4>
Normal Control Flow
Control Flow withContinuations
continuation
Continuation invoked
continuation
Isn't that just a glorified GOTO?
class ControlCentre
def exploreTerrainmarsPlateau = Plateau.new(5, 5)alpha = Rover.new("alpha")c = nilc = nilsourcesource == callcc do |cc| callcc do |cc|
c = cc c = cc Position.new(1, 2, "N")
endendalpha.land(marsPlateau, source)destination = alpha.explore("LMLMLMLMM")print destinationreturn creturn c
endend
> c = exploreTerrain“1 3 N”=> #<Continuation:0xb7466240>
> c.call(Position.new(2, 3, “E”))“2 4 E”=> #<Continuation:0xb7466240>
Normal Control Flow
Control Flow withContinuations
continuation
Continuation invokedwith different parameters
continuationWith different parameters
def continuationputs "i do some computation " +
"& save the state of execution."m = callcc { |cc| return cc }puts "then i restart the execution from " +
"the saved state & pass custom " + "values such as \"#{m[0]}\" and \"#{m[1]}\""
end
> c = continuation
i do some computation & save the state of execution.
> c.call("so..", "then..")
then i restart the execution from the saved state & pass custom values such as "so.." and "then.."
Normal Control Flow
Control Flow withContinuations paused
continuation
Continuation reinvokedwith parameters
continuationWith parameters
Continuation?
● A continuation reifies the control flow of a program as just another data value
● Reify – To consider an abstract concept to be real. To make control flow as first class functions/objects.
Continuation
● Continuation is an object that takes a snapshot copy of the running process.
● The current process is copied and saved.
● Current process – execution context and all local variables
Normal Control Flow
Control Flow marked with continuation
#
continuation
Memory
Local variables
A new process that invokes a continuation
#
Memory
The current execution is abandoned
Note: Global data does not get stored in a continuation
Continuations
● A Continuation stores the execution context, its references and pointers.
● However, it does not store the heap data or data that is outside the scope of local references and pointers.
● So any change made to them will stay changed even after invoking the continuation.
Continuations Sandwich Analogy
call-with-current-continuation
● call-with-current-continuation, in short called as “call/cc” is a procedure first introduced in Scheme.
● Now exists in several other programming languages such as Ruby, known as “callcc”
call-with-current-continuation
● call/cc takes in only one argument, which is a function f.
● It takes the current continuation as an object and applies f to it.
– continuation = new Continuation()
– function(continuation)
● When, callcc gets invoked, the program flow will continue from the point it was captured and the argument to the continuation will become its return value
Method definition:function: call/cc(function: f) {
c = new: Continuationf(c)
}
JavaScript (Rhino):function callcc(f) {
var c = new Continuation();return f(c);
}
Ruby:callcc {|cont|} cont => Continuation objectcont.call(*args)
call-with-current-continuation
call-with-current-continuationfunction callcc(fun) {
var continuation = new Continuation()return fun(continuation)
}
var ccprint(3 * (8 + callcc(function(c) {
cc = creturn 4
})))
js> 36
call-with-current-continuationfunction callcc(fun) {
var continuation = new Continuation()return fun(continuation)
}
var ccprint(3 * (8 + callcc(function(c) {
cc = creturn 4
})))
js> 36
js> cc(6)42
def continue_example i = 0 print "Before callcc #{i}\n" callcc do |cc| for i in 1..10 do print "In callcc #{i}\n" return cc if i > 5 end end print "After callcc #{i}\n"end
def continue_example i = 0 print "Before callcc #{i}\n" callcc do |cc| for i in 1..10 do print "In callcc #{i}\n" return cc if i > 5 end end print "After callcc #{i}\n"end
puts "begin control flow."c = continue_example()puts "after invoking the method"if c c.call puts "this will not get executed"end
def continue_example i = 0 print "Before callcc #{i}\n" callcc do |cc| for i in 1..10 do print "In callcc #{i}\n" return cc if i > 5 end end print "After callcc #{i}\n"end
puts "begin control flow."c = continue_example()puts "after invoking the method"if c c.call puts "this will not get executed"end
begin control flow.Before callcc 0In callcc 1In callcc 2In callcc 3In callcc 4In callcc 5In callcc 6after invoking the methodAfter callcc 6after invoking the method
call-with-current-continuation
● Continuations are sort of goto labels. But more powerful.● We can store the continuation in a variable or
object field, unlike a goto label.● Goto labels are restricted within a functional
scope, continuations are not limited by that.
The following code is strictly for session purposes only. Implementing this in usable code may cause
harm to life and limb.
Continuation as a GOTOdef goto_in_ruby_1 i = 0 callcc {|@label|} i = i + 1 print "#{i}\t" @label.call if i < 10end
Continuation as a GOTOdef goto_in_ruby_1 i = 0 callcc {|@label|} i = i + 1 print "#{i}\t" @label.call if i < 10end
output:1 2 3 4 5 6 7 8 9 10
Continuation as a GOTOdef goto_in_ruby_1 i = 0 callcc {|@label|} i = i + 1 print "#{i}\t" @label.call if i < 10end
output:1 2 3 4 5 6 7 8 9 10
def goto_in_ruby_2 i = 0 callcc do |label| label.call
i = 99endprint i
end
Continuation as a GOTOdef goto_in_ruby_1 i = 0 callcc {|@label|} i = i + 1 print "#{i}\t" @label.call if i < 10end
output:1 2 3 4 5 6 7 8 9 10
def goto_in_ruby_2 i = 0 callcc do |label| label.call
i = 99endprint i
end
output:0
Summary
● Continuation is a procedure that represents the remaining steps in a computation.
● It is a "snapshot" of the current control context or control state of the program
● The continuation can be later invoked (repeatedly) with custom arguments
Continuations
● Languages that support first class continuations● Scheme (call/cc)● Ruby (callcc)● Smalltalk (Continuation currentDo:)● Rhino [JavaScript] (new Continuation())● Scala (Responder & shift, reset)● Perl (Coro)● Haskell (Control.Monad.Cont)
Continuations Types
● First class continuations● Continuation Passing Style (CPS)● Delimited Continuations
Continuation Passing Style
● A function, instead of returning an explict value, it takes an explict 'continuation' argument – another function which is meant to receive the result of the computation
Continuation Passing Style
function (arg) {//computationreturn result;
}
function (arg, k) {//computationk(result);
}
function add(x, y) {return x + y
}
function square(x) {return x * x
}
function calculate(x, y) {return add(square(x), square(y))
}
js> print(calculate(3, 4))=> 25
CPS - Example
function add_cps(x, y, c) {c(x + y)
}
function square_cps(x, c) {c(x * x)
}
CPS - Example
function add_cps(x, y, c) {c(x + y)
}
function square_cps(x, c) {c(x * x)
}
function calculate_cps(x, y, c) {square_cps(x, function(v) {
square_cps(y, function(r){add_cps(v,r,c)
})})
}
CPS - Example
function add_cps(x, y, c) {c(x + y)
}
function square_cps(x, c) {c(x * x)
}
function calculate_cps(x, y, c) {square_cps(x, function(v) {
square_cps(y, function(r){add_cps(v,r,c)
})})
}
js> calculate_cps(3, 4, print)=> 25js> calculate_cps(3, 4, function(v) {print(sqrt(v))});=> 5
CPS - Example
CPS
● CPS is used in programming languages that do not have first – class continuations, but have first – class functions
● Every call is a tail call and if used without Tail Call Optimization (TCO), the continuation would grow during recursion
● Using CPS and TCO, compilers and interperters of some programming languages managed to eliminate the need for a runtime stack.
Delimited Continuations
● Delimited (or partial, or composable) continuations are more like regular functions and less like GOTOs. They do not embody the entire rest of the computation, but just a partial rest, up to a programmer-defined outer bound.
● delimited continuations will return control to the caller
after they are invoked, and they may also return values
– Tiark Rompf, Ingo Maier, Martin Odersky
Normal Control Flow
Control Flow withDelimited Continuations
Continuation -start
Continuation invoked
Continuation -end
Continuation -start
Delimitedcontinuation
Delimited Continuations
● Delimited Continuations are manipulated by means of two primitives, shift and reset.
● Calling shift captures the current continuation, and reset defines the boundary up to which the continuation reaches.
Delimited Continuationsreset {
shift { k: (Int=>Int) => k(7)
} + 1 } * 2
scala> 16
CPS transformed2 * (1 + 7)
Delimited Continuationsreset {
shift { k: (Int=>Int) => k(7)
} + 1 } * 2
scala> 16__________________________________________________________
reset {foo() + 1
} * 2
CPS transformed2 * (1 + 7)
def foo() {shift { k: (Int=>Int) =>
k(7)}
}
Delimited Continuationsreset{
1 + shift{ k: (Int=>Int) => k(k(7))
}} * 3
scala>27
reset{1 + shift{ k: (Int=>Int) =>
k(k(7))} * 3
}
scala>67
Delimited Continuationsreset{
1 + shift{ k: (Int=>Int) => k(k(7))
}} * 3
scala>27
reset{1 + shift{ k: (Int=>Int) =>
k(k(7))} * 3
}
scala>67
CPS Transform: (1 + (1 + 7)) * 3 = 27CPS Transform: (1 + (1 + 7)) * 3 = 27
CPS Transform: (1 + (1 + 7 * 3) * 3) = 67CPS Transform: (1 + (1 + 7 * 3) * 3) = 67
So... ummm..Whats the real world use of this thing?
Applications of Continuations
● Pause and Play e.g. User Interface design● Backtracking● Coroutines● Exception Handling● Web Frameworks● Compiler & Interpreter implementation
Problems
● Page centric● HTTP Stateless-ness causes heavy use of
session, cookies● 'Back' button in user interaction pages● Browser cloning in user interaction pages● Inversion of control
def add_two_numbers n1 = callcc { |cc| return ask("1st number:", cc) } n2 = callcc { |cc| return ask("2nd number:", cc) } print "sum of two numbers is #{n1 + n2}\n"end
def add_two_numbers n1 = callcc { |cc| return ask("1st number:", cc) } n2 = callcc { |cc| return ask("2nd number:", cc) } print "sum of two numbers is #{n1 + n2}\n"end
irb> add_two_numbers1st number:respond with: submit(25, ...): => #<Continuation:0xb76e0e58>irb> submit(25, 30)2nd number:respond with: submit(37, ...): => #<Continuation:0xb76ddde8>irb> submit(37, 12)sum of two numbers is 42=> nil
def add_two_numbers n1 = callcc { |cc| return ask("1st number:", cc) } n2 = callcc { |cc| return ask("2nd number:", cc) } print "sum of two numbers is #{n1 + n2}\n"end
@session = Hash.new
def ask(str, cc) r = rand(100).to_i print "#{str}\nrespond with: submit(#{r}, ...): " @session[r] = cc ccend
def submit(id, number) @session[id].call(number)End
server
call: page acall: page bcall: page c
client
Continuations lookup table (session)
1
2
3
4
Server renders the page and saves the continuation in the session with a uuid
The page is rendered with the uuid given in the
url
Client makes a server response
with required information
The continuation object is looked up based on the uuid
in the url and executed
Web Frameworks
● Seaside in Smalltalk (v 3.1)● Wee in Ruby (v 2.1)● Nagare in Python (v 0.3)● Apache Cocoon in Java (v 2.2)● RIFE in Java (v 1.6)● Ocsigen in OCaml (v 1.3)● Weblocks in Common Lisp
Web Servers with Continuations support
● Continuity (Perl)● Jetty 6 (Java)● Plt-Scheme web server (Scheme)
Seaside
● Written in Smalltalk
● Often called the 'heretic' framework because it doesn't follow the 'conventional' web wisdom
– Clean urls
– Templates to separate model and view
– Share as little state as possible
– Urls contain session identifiers
– There is no 'view' or html templates. Views are part of smalltalk objects
– Seaside continuations & callbacks give a stateful behavior
Seaside - Installation
One – click install experience
Seaside - Workspace
● Smalltalk deals with image snapshots for saving the workspace.
● There is no concept of 'files' and 'editors'
● Class objects are created through 'object browser' and code is executed through a 'workspace'
Seaside - Components
● Components represent the whole of a page or portion of pages
● The inherit from 'WAComponent' class
● Components maintain state
● Component's response loop– Renders the component
– Waits for user's input
– Executes a block of code when a link or button is pressed
– Re-renders itself or another component in place
Image from - Seaside – A Multiple Control Flow Web Application Framework, Stephane Ducasse
Seaside - callbacks
● Every user interaction on the web page triggers a callback.
● A callback can be on a link, button or any other form elements
● The callback will execute a block of code whose result will end in re-rendering the component or a new component
SimpleCounter>>initialize super initialize. counter := 0.
SimpleCounter>>renderContentOn: html html heading: 'the counter is ', counter asString. html anchor callback: [ counter := counter + 1 ]; with: 'increase'.
Seaside - Continuations
● Call / Answer● call: takes a component as a parameter and it swaps
the current component for the new component
● The new component will be rendered and user action will cause a callback to run that'll send an answer:
● The value passed to answer: will be returned to call: and the program will continue from there.
Image from - Seaside – A Multiple Control Flow Web Application Framework, Stephane Ducasse
Request 1
Response 1
Continuation saved
Request 2
Response 2
Continuation invoked
Problem with full continuation
Solved with delimited continuation
Request 1
Response 1
DelimitedContinuation saved
Request 2
Response 2
Delimited Continuation invoked
Challenges
● Understanding continuations as a concept ● Debugging● Buying into the 'heretic' idea
Seaside – Demo
References● http://en.wikipedia.org/wiki/Continuation
● http://idea-log.blogspot.com/2005/10/why-are-continuations-so-confusing-and.html
● http://www.iam.unibe.ch/~scg/Archive/Papers/Duca04eSeaside.pdf
● http://vimeo.com/13304075
● http://www.intertwingly.net/blog/2005/04/13/Continuations-for-Curmudgeons
● http://www.cs.indiana.edu/~dfried/appcont.pdf
● http://lambda-the-ultimate.org/node/86
● http://en.wikipedia.org/wiki/Continuation-passing_style
● http://lamp.epfl.ch/~rompf/continuations-icfp09.pdf
● http://book.seaside.st/book
● http://en.wikipedia.org/wiki/Call-with-current-continuation
● http://www.ibm.com/developerworks/java/library/j-contin.html
● http://www.cs.brown.edu/~sk/Publications/Papers/Published/khmgpf-impl-use-plt-web-server-journal/
● http://double.co.nz/pdf/continuations.pdf