View
9
Download
0
Category
Preview:
Citation preview
node.js?done.scala!Implementing Scalable Async IO using
Delimited Continuations
Tiark Rompf, EPFL
Saturday, June 4, 2011
who has heard about node.js?
Saturday, June 4, 2011
6/3/11 7:18 AMnode.js
Page 1 of 4http://nodejs.org/
Evented I/O for V8 JavaScript.
An example of a web server written in Node which responds with"Hello World" for every request.
var http = require('http');http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n');}).listen(1337, "127.0.0.1");console.log('Server running at http://127.0.0.1:1337/');
To run the server, put the code into a file example.js and executeit with the node program:
% node example.jsServer running at http://127.0.0.1:1337/
Here is an example of a simple TCP server which listens on port1337 and echoes whatever you send it:
var net = require('net');
var server = net.createServer(function (socket) { socket.write("Echo server\r\n"); socket.pipe(socket);});
DownloadChangeLogAboutv0.4.8 docs
WikiBlogCommunityDemoJobs
Hash InsightsChickRx
Saturday, June 4, 2011
purely asynchronous
callback driven
no blocking operations
good scalability
Saturday, June 4, 2011
6/3/11 7:22 AMGoogle Trends: erlang, node.js, clojure,
Page 1 of 1http://www.google.com/trends?q=erlang%2C+node.js%2C+clojure%2C&ctab=0&geo=all&date=all&sort=0
tiark.rompf@gmail.com | Signout
erlang, node.js, clojure,
Search Trends
Tip: Use commas to compare multiple search terms.
Searches Websites All regions All years
Scale is based on the average worldwide traffic of erlang in all years. Learn more
erlang 1.00 node.js 0.08 clojure 0.12
Rank by erlang
Scaling Large Projects With ErlangSlashdot - Jul 6 2008
The AZ of Programming Languages: ErlangComputerworld - Jun 16 2009
The AZ of Programming Languages: ClojureComputerworld - Aug 10 2009
Erlang Solutions, Ltd. and Basho Technologies, Inc. EnterBroad Partnership to Deliver Scalable, Fault TolerantApplications to a Global MarketCNNMoney.com - Mar 30 2010
Programming ClojureSlashdot - May 17 2010
Erlang and OTP in ActionSlashdot - Dec 8 2010
More news results »
Regions
1. Sweden
2. RussianFederation
3. SouthKorea
4. SouthAfrica
5. India
6. Singapore
7. Ukraine
8. Indonesia
9. China
10. Philippines
Cities
1. Goteborg,Sweden
2. Stockholm,Sweden
3.Moscow,RussianFederation
4. Beijing,China
5.SanFrancisco,CA, USA
6. Shanghai,China
7. Guangzhou,China
8. Seattle,WA, USA
9. Delhi, India
10. Bogota,Colombia
Languages
1. Swedish
2. Korean
3. Russian
4. Chinese
5. English
6. Indonesian
7. Danish
8. Hungarian
9. Japanese
10. German
Export this page as a CSV file
Google Trends provides insights into broad search patterns. Please keep in mind that several approximations areused when computing these results.
©2008 Google - Discuss - Terms of Use - Privacy Policy - Help
Saturday, June 4, 2011
why would Scala programmers care?
Saturday, June 4, 2011
#1: it’s about scalability
Saturday, June 4, 2011
#2: no equivalent Scala libs
Saturday, June 4, 2011
a little experiment: port it over to Scala!
Saturday, June 4, 2011
done.scala
• a minimal scala port of node.js
• only net, http and supporting modules
• very incomplete (and not at all done!)
Saturday, June 4, 2011
an interesting exercise:
JavaScript Scaladynamic language statically typed
Saturday, June 4, 2011
• added type annotations in a few places
• helper classes for anonymous objects{ host: ‘google.com’, port: 80 }
• overloading, default arguments and varargs instead of dynamic typeof
quite smooth overall:
Saturday, June 4, 2011
use Java NIO underneath
Saturday, June 4, 2011
http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}) res.end('Hello World\n')}).listen(8000, "127.0.0.1")console.log('server running')
http.createServer { (req, res) => res.writeHead(200, "Content-Type" -> "text/plain") res.end("Hello World\n")}.listen(8000, "127.0.0.1")println("server running")
JavaScript
Scala
Saturday, June 4, 2011
now what?
Saturday, June 4, 2011
node’s conscious choice:only callback-driven apis
but is it the right choice?
Saturday, June 4, 2011
small examples are fine but does it scale?
Saturday, June 4, 2011
println(“hello”)setTimeout(1000) {
println(“world”)}
Saturday, June 4, 2011
println(“hello”)setTimeout(1000) {
println(“world”)}
hello<pause>world
Saturday, June 4, 2011
setTimeout(1000) {println(“world”)
}println(“hello”)
Saturday, June 4, 2011
setTimeout(1000) {println(“world”)
}println(“hello”)
hello<pause>world
Saturday, June 4, 2011
for (x <- List(1,2,3) {setTimeout(1000) {
println(“found ” + x)}
}
Saturday, June 4, 2011
for (x <- List(1,2,3) {setTimeout(1000) {
println(“found ” + x)}
}
<pause>found 1found 2found 3
Saturday, June 4, 2011
try {setTimeout(1000) {
throw new Exception}
} catch {case e =>
println(“caught: ” + e)}
Saturday, June 4, 2011
try {setTimeout(1000) {
throw new Exception}
} catch {case e =>
println(“caught: ” + e)}
?Saturday, June 4, 2011
many things no longer work as expected
Saturday, June 4, 2011
callback-driven programmingis really hard
Saturday, June 4, 2011
val host = “http://search.twitter.com/”val query = “#scaladays”
http.get(host,80,“search.atom?q=”+query) { res => println(“STATUS:” + res.statusCode) res.onData { chunk => println(“BODY:” + chunk) } res.onEnd { println(“DONE”) }}
Saturday, June 4, 2011
val host = “http://search.twitter.com/”val query = “#scaladays”
http.get(host,80,“search.atom?q=”+query) { res => println(“STATUS:” + res.statusCode) res.onData { chunk => println(“BODY:” + chunk) } res.onEnd { println(“DONE”) }}
STATUS: 400BODY: <data>BODY: <more data>DONE
Saturday, June 4, 2011
val host = “http://search.twitter.com/”val queries = List(“#scala”,“#scaladays”, “@odersky”)
val results = for (q <- queries) yield { http.get(host,80,“search.atom?q=”+query) { res => ... }}
println(results)
Saturday, June 4, 2011
val host = “http://search.twitter.com/”val queries = List(“#scala”,“#scaladays”, “@odersky”)
val results = for (q <- queries) yield { http.get(host,80,“search.atom?q=”+query) { res => ... }}
println(results)
List(ClientRequest, ClientRequest, ClientRequest)
Saturday, June 4, 2011
can we do something about it?
Saturday, June 4, 2011
execute asynchronously
program synchronously
Saturday, June 4, 2011
scalac -P:continuations:enable
Saturday, June 4, 2011
def foobar: Int @suspendable = shift { retrn: (Int=>Unit) => retrn(7)}
def abort: Unit @suspendable = shift { retrn: (Unit=>Unit) => // just don’t return...
}
reset {println(“A”)abortprintln(“B”)
}println(“C”)
Saturday, June 4, 2011
def foobar: Int @suspendable = shift { retrn: (Int=>Unit) => retrn(7)}
def abort: Unit @suspendable = shift { retrn: (Unit=>Unit) => // just don’t return...
}
reset {println(“A”)abortprintln(“B”)
}println(“C”)
AC
Saturday, June 4, 2011
def setTimeout(delay: Int)(callback: => Unit): Unit = ...
println(“hello”)setTimeout(1000) {
println(“world”)}
Saturday, June 4, 2011
def setTimeout(delay: Int)(callback: => Unit): Unit = ...
println(“hello”)setTimeout(1000) {
println(“world”)}
hello<pause>world
Saturday, June 4, 2011
def setTimeout(delay: Int)(callback: => Unit): Unit = ...
def sleep(delay: Int) = shift { retrn: (Unit=>Unit) => setTimeout {
retrn()}
}
println(“hello”)setTimeout(1000) {
println(“world”)}
println(“hello”)sleep(1000)println(“world”)
hello<pause>world
Saturday, June 4, 2011
def setTimeout(delay: Int)(callback: => Unit): Unit = ...
def sleep(delay: Int) = shift { retrn: (Unit=>Unit) => setTimeout {
retrn()}
}
println(“hello”)setTimeout(1000) {
println(“world”)}
println(“hello”)sleep(1000)println(“world”)
hello<pause>world
hello<pause>world
Saturday, June 4, 2011
http.get(...) { res => res.onData { chunk => println(“BODY:” + chunk) } res.onEnd { println(“DONE”) }}
Saturday, June 4, 2011
http.get(...) { res => res.onData { chunk => println(“BODY:” + chunk) } res.onEnd { println(“DONE”) }}
val res = http.get(...)
for (chunk <- res.data) { println(“BODY:” + chunk)}
println(“DONE”)
Saturday, June 4, 2011
http.get(...) { res => res.onData { chunk => println(“BODY:” + chunk) } res.onEnd { println(“DONE”) }}
val res = http.get(...)
for (chunk <- res.data) { println(“BODY:” + chunk)}
println(“DONE”)
def data = new { def foreach(yld: Buffer=>Unit) = shift { retrn: (Unit=>Unit) =>
onData(yld)onEnd(retrn)
}def mkString = {
val s = new StringBuilder; foreach(s+=_); s.result}
}Saturday, June 4, 2011
val host = “http://search.twitter.com/”val queries = List(“#scala”,“#scaladays”, “@odersky”)
val results = for (q <- queries) yield {val res = http.get(host,80,“search.atom?q=”+query)val xml = XML.loadString(res.data.mkString)xml \ "entry" \ "title" text
}
println(results)
Saturday, June 4, 2011
val host = “http://search.twitter.com/”val queries = List(“#scala”,“#scaladays”, “@odersky”)
val results = for (q <- queries) yield {val res = http.get(host,80,“search.atom?q=”+query)val xml = XML.loadString(res.data.mkString)xml \ "entry" \ "title" text
}
println(results)
<console>:11: error: no type parameters for method map: (f: (java.lang.String) => B)(implicit bf: scala.collection.generic.CanBuildFrom[List[java.lang.String],B,That])That exist so that it can be applied to arguments ((java.lang.String) => String @scala.util.continuations.cpsParam[Unit,Unit])
Saturday, June 4, 2011
val host = “http://search.twitter.com/”val queries = List(“#scala”,“#scaladays”, “@odersky”)
val results = for (q <- queries.suspendable) yield {val res = http.get(host,80,“search.atom?q=”+query)val xml = XML.loadString(res.data.mkString)xml \ "entry" \ "title" text
}
println(results)
Saturday, June 4, 2011
val host = “http://search.twitter.com/”val queries = List(“#scala”,“#scaladays”, “@odersky”)
val results = for (q <- queries.suspendable) yield {val res = http.get(host,80,“search.atom?q=”+query)val xml = XML.loadString(res.data.mkString)xml \ "entry" \ "title" text
}
println(results)
List(List(“#Scala highlighted in article from @TheEconomist on parallel programing and multicore: http://econ.st/jkGchu @typesafe”, ...), List(“There are so many great talks today at #scaladays; it's hard to decide which to attend!”, ...), List(“@odersky is there any info on Cascade up somewhere?”, ...))
Saturday, June 4, 2011
implicit def richIterable[A,Repr](xs: IterableLike[A,Repr]) = new { def suspendable = new {
def foreach(yld: A => Unit @suspendable): Unit @suspendable = {val it = xs.iteratorwhile (it.hasNext) yld(it.next)
}def map[B, That](f: A => B @suspendable)
(implicit bf: CanBuildFrom[Repr, B, That]): That @suspendable = {val b = bf(xs.repr)foreach(x => b += f(x))b.result
}}
}
val results = for (q <- List(1,2,3).suspendable) yield {sleep(1000); println(“tick”)
}
Saturday, June 4, 2011
implicit def richIterable[A,Repr](xs: IterableLike[A,Repr]) = new { def suspendable = new {
def foreach(yld: A => Unit @suspendable): Unit @suspendable = {val it = xs.iteratorwhile (it.hasNext) yld(it.next)
}def map[B, That](f: A => B @suspendable)
(implicit bf: CanBuildFrom[Repr, B, That]): That @suspendable = {val b = bf(xs.repr)foreach(x => b += f(x))b.result
}}
}<pause>tick<pause>tick<pause>tick
val results = for (q <- List(1,2,3).suspendable) yield {sleep(1000); println(“tick”)
}
Saturday, June 4, 2011
val host = “http://search.twitter.com/”val queries = List(“#scala”,“#scaladays”, “@odersky”)
val results = for (q <- queries.suspendable) yield {println(“start ”+q)val res = http.get(host,80,“search.atom?q=”+query)val xml = XML.loadString(res.data.mkString)println(“done ”+q)xml \ "entry" \ "title" text
}
Saturday, June 4, 2011
val host = “http://search.twitter.com/”val queries = List(“#scala”,“#scaladays”, “@odersky”)
val results = for (q <- queries.suspendable) yield {println(“start ”+q)val res = http.get(host,80,“search.atom?q=”+query)val xml = XML.loadString(res.data.mkString)println(“done ”+q)xml \ "entry" \ "title" text
}
start #scaladone #scalastart #scaladaysdone #scaladaysstart @oderskydone @odersky
Saturday, June 4, 2011
def spawn(body: => Unit @suspendable) = {eventLoop.submitTask(() => reset(body))
}
class DataFlowCell[A] {private[this] var value: Option[A] = Noneprivate[this] var queue: List[A=>Unit] = Nil
def apply() = shift { retrn: (A=>Unit) =>value match {
case Option(v) => retrn(v)case None => queue ::= retrn
}}
def set(v: A) = value match {case Option(_) => assert(false, “can’t set value twice”)case None => value = Some(v); queue.foreach(f => spawn(f(v)))
}}
Saturday, June 4, 2011
def future[A](body: => A @suspendable) = {val cell = new DataFlowCell[A]spawn { cell.set(body) }cell
}
def par[A,B](a: => A @suspendable)(b: => B @suspendable) = {val (u,v) = (future(a), future(b))(u(),v())
}
par { sleep(1000); println(“a”)
} { sleep(1000); println(“b”)
}println(“done”)
Saturday, June 4, 2011
def future[A](body: => A @suspendable) = {val cell = new DataFlowCell[A]spawn { cell.set(body) }cell
}
def par[A,B](a: => A @suspendable)(b: => B @suspendable) = {val (u,v) = (future(a), future(b))(u(),v())
}
par { sleep(1000); println(“a”)
} { sleep(1000); println(“b”)
}println(“done”)
<pause>badone
Saturday, June 4, 2011
implicit def richParIterable[A,Seq,Repr](xs: ParIterableLike[A,Seq,Repr]) = new {def suspendable = new {
def foreach(yld: A => Unit @suspendable): Unit @suspendable = {val futures = xs.seq.map(x => future(yld(x)) // sequential list of futuresfutures.suspendable.foreach(_.apply())
}def map[B, That](f: A => B @suspendable)(... bf ...): That @suspendable = {
val futures = xs.seq.map(x => future(yld(x))futures.suspendable.map(_.apply())
}}
}
val results = for (q <- List(1,2,3).par.suspendable) yield {sleep(1000); println(“tick”)
}println(“done”)
Saturday, June 4, 2011
implicit def richParIterable[A,Seq,Repr](xs: ParIterableLike[A,Seq,Repr]) = new {def suspendable = new {
def foreach(yld: A => Unit @suspendable): Unit @suspendable = {val futures = xs.seq.map(x => future(yld(x)) // sequential list of futuresfutures.suspendable.foreach(_.apply())
}def map[B, That](f: A => B @suspendable)(... bf ...): That @suspendable = {
val futures = xs.seq.map(x => future(yld(x))futures.suspendable.map(_.apply())
}}
}
<pause>tickticktickdone
val results = for (q <- List(1,2,3).par.suspendable) yield {sleep(1000); println(“tick”)
}println(“done”)
Saturday, June 4, 2011
val host = “http://search.twitter.com/”val queries = List(“#scala”,“#scaladays”, “@odersky”)
val results = for (q <- queries.par.suspendable) yield {println(“start ”+q)val res = http.get(host,80,“search.atom?q=”+query)val xml = XML.loadString(res.data.mkString)println(“done ”+q)xml \ "entry" \ "title" text
}
Saturday, June 4, 2011
val host = “http://search.twitter.com/”val queries = List(“#scala”,“#scaladays”, “@odersky”)
val results = for (q <- queries.par.suspendable) yield {println(“start ”+q)val res = http.get(host,80,“search.atom?q=”+query)val xml = XML.loadString(res.data.mkString)println(“done ”+q)xml \ "entry" \ "title" text
}
start #scalastart #scaladaysstart @oderskydone #scaladone #scaladaysdone @odersky
Saturday, June 4, 2011
Conclusions
• Scala needs good networking libs
• asynchronous execution does not meancallback-driven programming
• continuations can re-introduce synchronictiy
Saturday, June 4, 2011
Recommended