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 implementation of the Practical Effect system is developed as a compiler plugin for the Scala programming language. The plugin is based on the plugin developed by Rytz et al and is composed of two sub plugins to implement bidirectional checking: the effect inference plugin, and the effect checking plugin. The effect inference plugin is a modification of the one developed by Rytz et al, extended with support for gradual effects and the customizable effect system. As explained before, in relation to bidirectional checking, Scala has inference of effect, therefore there are cases where there is absence of effect annotations. The effect inference plugin is necessary to annotate function abstractions that do not have effect annotations. This information about effect inference is used by the effect checking plugin to check and adjust sets of effect privileges, also inserting runtime checks of effect wherever it may be necessary.
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() => prod @simpleNoIO
def views.html.foo.apply[T]() => prod @simpleNoIO
call scala.Predef.read*: T => prod @simpleInput
call fakePrint(T <: String) => prod @simpleOutput
call readM => prod @simpleInput
def scala.Predef.read*(V): T => prod @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 produce @simpleOutput. We can test this by calling method “readInt” which produces @simpleInput according to the effscript file:
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 “gpes” 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 a “benchmark run” like this (edit this file to indicate the appropriate path for Scala binaries and libraries):
./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 systemCollsSimple/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.

