HelloWorld++
def run() = {
println("What is your name?")
val name = readln()
println("Hello, " + name + "!")
}
Look Ma, a continuation!
def run() = {
println("What is your name?")
val name = readln()
println("Hello, " + name + "!")
}
Pull it aside
def run() = {
println("What is your name?")
val name = readln()
println("Hello, " + name + "!")
}
val k = { name: String =>
println("Hello, " + name + "!")
}
Continuation-passing style
val k = { name: String =>
println("Hello, " + name + "!")
}
def run() = {
println("What is your name?")
k(readln())
}
Shift and reset
def run() = reset {
println("What is your name?")
val name = shift { k: (String => Unit) =>
val name = readln()
k(name)
}
println("Hello, " + name + "!")
}
Procrastinate
var c: String => Unit = _
def run() = reset {
println("What is your name?")
val name = shift { k: (String => Unit) =>
c = k
}
println("Hello, " + name + "!")
}
Do it live
scala> run()
What is your name?
scala> c("James")
Hello, James!
scala>
This code sure is ugly
def run() = reset {
println("What is your name?")
val name = shift { k: (String => Unit) =>
c = k
}
println("Hello, " + name + "!")
}
var c: String => Unit = _
Pull aside the shift
def run() = reset {
println("What is your name?")
val name = prompt()
println("Hello, " + name + "!")
}
var c: String => Unit = _
def prompt() = shift {
k: (String => Unit) => c = k
}
Now we can cleanly prompt twice
var c: String => Unit = _
def prompt() = shift {
k: (String => Unit) => c = k
}
def run() = reset {
println("What is your name?)
val name = prompt()
println("How old are you?)
val age = prompt()
println("Hello, " + name + "!")
println("You are " + age + " years old.")
}
Two continuations in one
var c: String => Unit = _
def prompt() = shift {
k: (String => Unit) => c = k
}
def run() = reset {
println("What is your name?)
val name = prompt()
println("How old are you?)
val age = prompt()
println("Hello, " + name + "!")
println("You are " + age + " years old.")
}
Back to the REPL
scala> run()
What is your name?
scala> c("James")
How old are you?
scala> c("29")
Hello, James!
You are 29 years old.
scala>
Clear, imperative workflow
def run() = {
println("What is your name?")
val name = readln()
println("How old are you?")
val age = readln()
println("Hello, " + name + "!")
println("You are " + age + " years old.")
}
Replace the terminal with a browser
def run(): NodeSeq = reset {
val name = prompt(<input name="Name" />)
val age = prompt(<input name="Age" />)
<h1>Hello { name }!<h1>
<p>You are { age } years old.</p>
}
Our familiar double-continuation
def run(): NodeSeq = reset {
val name = prompt(<input name="Name" />)
val age = prompt(<input name="Age" />)
<h1>Hello { name }!<h1>
<p>You are { age } years old.</p>
}
Our familiar double-continuation
def run(): NodeSeq = reset {
val name = prompt(<input name="Name" />)
val age = prompt(<input name="Age" />)
<h1>Hello { name }!<h1>
<p>You are { age } years old.</p>
}
var step: (String => NodeSeq) = _ => run()
def prompt(html: NodeSeq): String @imp =
shift { k: (String => NodeSeq) =>
step = k
html
}
Scalatra kicks things off
get("/") { step(params("input")) }
More complex forms
def getNameAndAge(
params: Map[String, String] = Map.empty):
(String, String) @imp = { ... }
def getBook(
params: Map[String, String] = Map.empty):
String @imp = { ... }
def run(): NodeSeq = reset {
val (name, age) = getNameAndAge()
val book = getBook()
<h1>Hello { name }!<h1>
<p>You are { age } years old.</p>
<p>Your favorite book is { book }.</p>
}
Validation and re-prompting
def getBook(
params: Map[String, String]):
String @imp =
params.get("Book") match {
case None => prompt(...)
case Some(book) => book
}
Demo
State encoded in the continuation
def getItems(
params: Map[String, String] = Map.empty,
cart: List[Item] = Nil):
List[Item] @imp = { ... }
^D