-
Notifications
You must be signed in to change notification settings - Fork 5
Relative Effects
The effect of a higher-order function depends on the effect of its argument: for example, the method map which applies a function to all elements of a collection has the effect of the function that is passed into it.
Such functions are said to be effect-polymorphic, and their effects are annotated using relative effect annotations:
def h(f: Int => Int): Int @pure(f) = f(1)The higher-order method h has a relative effect annotation @pure(f) which states that h is pure, but it has the effect of its argument function f.
When invoking the method h, the effect of the invocation depends on the argument function:
def pure : Int @pure = h(x => x + 1)
def withIo: Int @pure @io = h(x => {println(x); x + 1})Relative effect annotations always apply to every effect domain.
Relative effect annotations are not tied to function types in any way, they work on parameters of arbitrary type:
def log(a: Any): Unit @pure(a.toString) @io = {
if (shouldLog) println(a.toString)
}For methods that take parameters, the arguments used in the @pure annotation are ignored. For convenience, the package object annotation.effects defines a value % of type Nothing which can be used.
def f(a: A): B @pure(a.foo(%)) = {
a.foo(1)
}For annotations of the form @pure(a) where no method of a is selected, the method named apply is used. Therefore effect-polymorphism with function parameters can be simply annotated @pure(f), which is equivalent to @pure(f.apply(%, %, ...)).
More details on relative effects can be found in the technical report "Relative Effect Declarations for Lightweight Effect-Polymorphism" (pdf).