Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master

Fetching latest commit…

Octocat-spinner-32-eaf2f5

Cannot retrieve the latest commit at this time

..
Octocat-spinner-32 AsyncConsoleIO.scala
Octocat-spinner-32 README.md
README.md

Asynchronous Console I/O with Scala Continuations

13 Jun 2011

Scala's delimited continuations allow for some interesting tricks of control flow, which can useful for building asynchronous I/O while writing in a synchronous style. This example shows how a while loop containing a console read and write can be turned inside-out into non-blocking code.

The Greeter trait below defines a prompt method to read a line of input from stdin, and a greet method which simulates an expensive write operation by sleeping before writing to stdout.

trait Greeter extends App {

  def prompt() = {
    println("name> ")
    readLine
  }

  def greet(name: String) = {
    Thread.sleep(1000)
    println("hello, " + name)
  }
}

The SyncConsoleIO object makes multiple calls to prompt and greet in the conventional way; using a while loop to make consecutive calls to each.

object SyncConsoleIO extends Greeter {

  var i = 5
  while (i > 0) {
    val name = prompt
    greet(name)
    i -= 1
  }
}

Running this code produces the following output. Note that each time hello, ... is printed, it is delayed by one second.

scala> SyncConsoleIO.main(Array[String]())
name> 
hello, Johannes
name> 
hello, Isaac
name> 
hello, Albert
name> 
hello, Richard
name> 
hello, Alan

A disadvantage of this approach is that each call to greet blocks each subsequent call to prompt, meaning the user can't provide any input until the previous output is complete. A way around this is to wrap each call in a function which kicks off a Thread, but this introduces tricky and noisy threading logic which is not strictly necessary.

Scala continuations provide a way around this by capturing each output in a continuation which occurs after each input. The AsyncConsoleIO does this in the shiftIt method.

type cont = cpsParam[Unit, Unit]
object AsyncConsoleIO extends Greeter {

  def shiftIt() = shift { k: (Unit => Unit) =>
    val name = prompt
    k()
    greet(name)
  }

  reset {
    var i = 5
    while (i > 0) {
      shiftIt()
      i -= 1
    } 
  }
}

Running this code produces the following output. Note that, as before, each time hello, ... is printed, it is delayed by one second. The difference this time is that it does not block user input.

scala> AsyncConsoleIO.main(Array[String]())
name> 
name> 
name> 
name> 
name> 
hello, Alan
hello, Richard
hello, Albert
hello, Isaac
hello, Johannes

The while loop used here is a bit contrived. I'm working on putting together a more monadic solution which will support methods from foreach to flatMap.

Something went wrong with that request. Please try again.