Delimited Continuations

import scala.util.continuations.cpsParam
import scala.util.continuations.reset
import scala.util.continuations.shift

import scala.collection.mutable.Stack

private val ks: Stack[HasEnv with HasReadLn with HasWrite => Unit] =
  Stack.empty[HasEnv with HasReadLn with HasWrite => Unit]
trait HasEnv {
  def env: Map[String, String]
}

def readEnv(name: String): String@cpsParam[Unit, Unit] =
  shift { (k: String => Unit) =>
    ks.push({ r: HasEnv => k(r.env(name)) })
    ()
  }
trait HasReadLn {
  def readLn(): String
}

def readLn(): String@cpsParam[Unit, Unit] =
  shift { (k: String => Unit) =>
    ks.push({ r: HasReadLn => k(r.readLn()) })
    ()
  }
trait HasWrite {
  def write(output: String): Unit
}

def write(output: String): Unit@cpsParam[Unit, Unit] =
  shift { (k: Unit => Unit) =>
    ks.push({ r: HasWrite =>
      r.write(output)
      k()
    })
    ()
  }
def enProgram(): Unit@cpsParam[Unit, Unit] = {
  write("What's your name? ")
  val name: String = readLn()
  write(s"Hello, ${name}!\n")
}

def esProgram(): Unit@cpsParam[Unit, Unit] = {
  write("¿Cómo te llamas? ")
  val name: String = readLn()
  write(s"¡Hola, ${name}!\n")
}

def program(): Unit@cpsParam[Unit, Unit] = {
  val lang: String = readEnv("LANG")
  if (lang.startsWith("es")) {
    esProgram()
  } else {
    enProgram()
  }
}
val r: HasEnv with HasReadLn with HasWrite =
  new HasEnv with HasReadLn with HasWrite {
    override val env: Map[String, String] = sys.env
    override def readLn(): String = scala.io.StdIn.readLine()
    override def write(output: String): Unit = print(output)
  }

reset {
  program()
}
while (ks.nonEmpty) {
  val k: HasEnv with HasReadLn with HasWrite => Unit = ks.pop()
  k(r)
}

sbt configuration

See the compiler plugin support section of the sbt documentation for the latest configuration information.

For sbt 1.0 and Scala 2.12, use the following:

/***
scalaVersion := "2.12.2"

addCompilerPlugin( "org.scala-lang.plugins"
                 % "scala-continuations-plugin_2.12.2"
                 % "1.0.3"
                 )

libraryDependencies +=
  "org.scala-lang.plugins" %% "scala-continuations-library" % "1.0.3"

scalacOptions += "-P:continuations:enable"
*/

Demo

This file is literate Scala, and can be run using Codedown:

$ curl https://earldouglas.com/posts/effect-systems/cps.md |
  codedown scala > script.scala
$ LANG=es sbt -Dsbt.main.class=sbt.ScriptMain script.scala
¿Cómo te llamas? James
¡Hola, James!