This is an old revision of the document!
EffScript: Practical Effects for Scala
EffScript is a small domain-specific language for writing tailored effect disciplines for Scala. In addition to being customizable, the underlying effect system supports both effect polymorphism (as developed by Lukas Rytz in his PhD thesis) and gradual effect checking (following the theory of Bañados, Garcia and Tanter).
Scala Implementation
The code and examples can be downloaded here. To run the examples, you need to have installed Scala (http://www.scala-lang.org/) and SBT (http://www.scala-sbt.org/).
Compiling and packaging the inference and checking library
For the effect inference library:
cd efftp sbt package
For the effect checking library:
cd gpes sbt package
How to use the compiler plugins
Let us create new project that will use the effect system with a custom “simpleIO” discipline (see examples/simpleUI.eff file):
name: simpleIO
privileges:
@simpleNoIO
@simpleOutput
@simpleInput
lattice:
top: @simpleOutput @simpleInput
bottom: @simpleNoIO
pointcuts:
def views.html.dummy.apply() => restrict @simpleNoIO
def views.html.foo.apply[T]() => restrict @simpleNoIO
call scala.Predef.read*: T => set @simpleInput
call fakePrint(T <: String) => set @simpleOutput
call readM => set @simpleInput
def scala.Predef.read*(V): T => set @simpleInput
We first create the folder for our new project which we call “playground” (project is included with examples of use):
mkdir playground
We need to link the compiler libraries to our new project. At the root of the new project:
cd playground mkdir lib ln -s [Absoule-path]/efftp/target/scala-2.10/effects-plugin_2.10-0.1-SNAPSHOT.jar lib/infer.jar ln -s [Absolute-path]/gpes/target/scala-2.10/effects-checker-plugin_2.10-0.1-SNAPSHOT.jar lib/check.jar
We could also have copied the libraries but, modifications to the discipline would implied a copy after every modification.
Next we need to edit a build.sbt file to declare the project name, Scala version, libraries and compiler options.
The effect plugin works with Scala 2.10.3, which can be specified in every project by editing a build.sbt file at the root of the project.
Our build.sbt file looks like this:
name := "playground" version := "0.1" autoCompilerPlugins := true scalaVersion := "2.10.4" libraryDependencies ++= List( "org.scala-lang" % "scala-compiler" % "2.10.3" ) scalacOptions += "-Xplugin:lib/infer.jar" scalacOptions += "-Xplugin:lib/check.jar" scalacOptions += "-P:effects:domains:simpleIO" scalacOptions += "-P:effects:unchecks:java.*:scala.(?!Function)*"
Next, let us create a file “helloGE.scala” to play with the effect system inside folders “src/main/scala/”
helloGE.scala looks like this:
import scala.annotation.effects._ object HelloGradualEffects{ def main(args: Array[String]): Unit @simpleOutput = { println("hello gradual effects") } }
We only have permissions to do @simpleOutput. We can test this by calling method “readInt”:
def main(args: Array[String]): Unit @simpleOutput = { println("hello gradual effects") readInt() }
Which gives a static error. Now let us test gradual effects:
def main(args: Array[String]): Unit @simpleOutput @pure= { def foo: Int @unknown = { readInt() } println("hello gradual effects") foo }
This code gives a runtime error: “Not enough privileges simpleInput”.
Modifying a discipline
Let us modify the simpleIO discipline defined in the previous section. Let us remember that the file can be found in “examples/simpleIO.eff”.
To modify the discipline, just edit the “simpleIO.eff” file, and then run
cd examples ./updateSimpleIODomain
Let us look at updateSimpleIODomain:
First it runs a script that uses effscript to generate the classes needed for the compiler plugins:
/usr/share/scala/bin/scala -cp ../effscript/target/scala-2.10/effscript_2.10-0.1-SNAPSHOT.jar updateDomain.scala simpleIO.eff
then it copies the generated files into each of the compiler plugins, overriding the existing implementations with the new ones:
cp target/SimpleIO.scala ../gpes/src/main/scala/annotation/effects/SimpleIO.scala cp target/SimpleIO.scala ../efftp/src/main/scala/scala/annotation/effects/SimpleIO.scala cp target/SimpleIODomain\(check\).scala ../gpes/src/main/scala/simpleIO/SimpleIODomain.scala cp target/SimpleIODomain\(infer\).scala ../efftp/src/main/scala/scala/tools/nsc/effects/simpleIO/SimpleIODomain.scala
After that step, we need to re-package both compiler plugins updating the .jar files.
Creating a new discipline
To create a new discipline we need to do extra steps. Let us suppose we want to add a new discipline called “newDisc”. Once we have created our newDisc.eff file we need to create the folders for this discipline in every compiler plugin project:
mkdir gpes/src/main/scala/newDisc mkdir efftp/src/main/scala/tools/nsc/effects/newDisc
Then create and run a batch just like the previous step to generate the files and copy them into each compiler plugin project.
Next, we need to add this discipline into each of the compiler plugin projects:
For “goes” we need to edit “src/main/scala/EffectsCheckerPlugin.scala” file, and add a new case inside “mkDomains” function:
...
case "simpleTPIO" :: xs =>
new simpletpio.SimpleTPIODomain {
val global: EffectChecker.this.global.type = EffectChecker.this.global
override val uncheckedFunctions = settings.unchecks
} :: mkDomains(xs)
case "newDisc" :: xs =>
new newdisc.newDiscDomain {
val global: EffectChecker.this.global.type = EffectChecker.this.global
override val uncheckedFunctions = settings.unchecks
} :: mkDomains(xs)
case x :: xs => mkDomains(xs)
For “efftp” we need to edit “src/main/scala/tools/nsc/effects/EffectsPlugin.scala” file, and add a new case inside “mkDomains” function:
...
case "simpleTPIO" :: xs =>
new simpletpio.SimpleTPIODomain {
val global: EffectsPlugin.this.global.type = EffectsPlugin.this.global
override val uncheckedFunctions = efftpSettings.unchecks
} :: mkDomains(xs)
case "newDisc" :: xs =>
new newdisc.newDiscDomain {
val global: EffectsPlugin.this.global.type = EffectsPlugin.this.global
override val uncheckedFunctions = efftpSettings.unchecks
} :: mkDomains(xs)
case x :: xs =>
global.abort(s"Unknown effect domain: $x")
Next, just re package each of the compiler plugin projects and update libraries of every project accordingly (unless symbolic links are being used).
Play project with effects
To illustrate a simple example of using effects with a Play application we provide an example project:
cd play-slick-advanced-effects ./activator
Once inside the activator console type “run” to run the application. The url to check the application running is http://localhost:9000
See app/controllers/EffectController.scala and app/views/index.scala.html for more information.
Running Benchmarks
For benchmarks, it is better to run the code not using sbt because using sbt the execution is slower. Benchmarks are inside “benchmarks” folder. We can run benchmark run like this:
./runExperiment [jar] [version]
Where [version] can be:
- 0 ⇒ 95 dynamic checks and 67 context adjustements
- 1 ⇒ 35 dynamic checks and 67 context adjustements
- 2 ⇒ 35 dynamic checks and 67 context adjustements
- 3 ⇒ fully annotated (do not produce runtime effect checks)
The [jar] can be:
- CollsNoeffs/target/scala-2.10/collsnoeffs_2.10-0.1.jar ⇒ without the effect system
- CollsSimple/target/scala-2.10/collssimple_2.10-0.1.jar ⇒ default effect system.
- CollsSimpleBits/target/scala-2.10/collssimple_2.10-0.1.jar ⇒ default effect system using bit vectors.
- CollsSimpleSE/target/scala-2.10/collssimplese_2.10-0.1.jar ⇒ scenario with subeffecting.
- CollsSimpleTP/target/scala-2.10/collssimpletp_2.10-0.1.jar ⇒ scenario with subeffecting and type parameters.
For example, to run the default effect system with the version with most dynamic checks:
./runExperiment CollsSimple/target/scala-2.10/collssimple_2.10-0.1.jar 0
The program returns the number of second of the benchmark.

