Experimental Definitions
The @experimental
annotation allows the definition of an API that is not guaranteed backward binary or source compatibility. This annotation can be placed on term or type definitions.
References to experimental definitions
Experimental definitions can only be referenced in an experimental scope. Experimental scopes are defined as follows:
-
The RHS of an experimental
def
,val
,var
,given
ortype
is an experimental scope. Examples:Example 1
import scala.annotation.experimental @experimental def x = () def d1 = x // error: value x is marked @experimental and therefore ... @experimental def d2 = x val v1 = x // error: value x is marked @experimental and therefore ... @experimental val v2 = x var vr1 = x // error: value x is marked @experimental and therefore ... @experimental var vr2 = x lazy val lv1 = x // error: value x is marked @experimental and therefore ... @experimental lazy val lv2 = x
Example 2
import scala.annotation.experimental @experimental val x = () @experimental def f() = () @experimental object X: def fx() = 1 def test1: Unit = f() // error: def f is marked @experimental and therefore ... x // error: value x is marked @experimental and therefore ... X.fx() // error: object X is marked @experimental and therefore ... import X.fx fx() // error: object X is marked @experimental and therefore ... @experimental def test2: Unit = // references to f, x and X are ok because `test2` is experimental f() x X.fx() import X.fx fx()
Example 3
import scala.annotation.experimental @experimental type E type A = E // error type E is marked @experimental and therefore ... @experimental type B = E
Example 4
import scala.annotation.experimental @experimental class A @experimental type X @experimental type Y = Int @experimental opaque type Z = Int def test: Unit = new A // error: class A is marked @experimental and therefore ... val i0: A = ??? // error: class A is marked @experimental and therefore ... val i1: X = ??? // error: type X is marked @experimental and therefore ... val i2: Y = ??? // error: type Y is marked @experimental and therefore ... val i2: Z = ??? // error: type Y is marked @experimental and therefore ... ()
Example 5
@experimental trait ExpSAM { def foo(x: Int): Int } def bar(f: ExpSAM): Unit = {} // error: error form rule 2 def test: Unit = bar(x => x) // error: reference to experimental SAM ()
-
The signatures of an experimental
def
,val
,var
,given
andtype
, or constructors ofclass
andtrait
are experimental scopes. Examples:Example 1
import scala.annotation.experimental @experimental def x = 2 @experimental class A @experimental type X @experimental type Y = Int @experimental opaque type Z = Int def test1( p1: A, // error: class A is marked @experimental and therefore ... p2: List[A], // error: class A is marked @experimental and therefore ... p3: X, // error: type X is marked @experimental and therefore ... p4: Y, // error: type Y is marked @experimental and therefore ... p5: Z, // error: type Z is marked @experimental and therefore ... p6: Any = x // error: def x is marked @experimental and therefore ... ): A = ??? // error: class A is marked @experimental and therefore ... @experimental def test2( p1: A, p2: List[A], p3: X, p4: Y, p5: Z, p6: Any = x ): A = ??? class Test1( p1: A, // error p2: List[A], // error p3: X, // error p4: Y, // error p5: Z, // error p6: Any = x // error ) {} @experimental class Test2( p1: A, p2: List[A], p3: X, p4: Y, p5: Z, p6: Any = x ) {} trait Test1( p1: A, // error p2: List[A], // error p3: X, // error p4: Y, // error p5: Z, // error p6: Any = x // error ) {} @experimental trait Test2( p1: A, p2: List[A], p3: X, p4: Y, p5: Z, p6: Any = x ) {}
-
The
extends
clause of an experimentalclass
,trait
orobject
is an experimental scope. Examples:Example 1
import scala.annotation.experimental @experimental def x = 2 @experimental class A1(x: Any) class A2(x: Any) @experimental class B1 extends A1(1) class B2 extends A1(1) // error: class A1 is marked @experimental and therefore marked @experimental and therefore ... @experimental class C1 extends A2(x) class C2 extends A2(x) // error def x is marked @experimental and therefore
-
The body of an experimental
class
,trait
orobject
is an experimental scope. Examples:Example 1
import scala.annotation.experimental @experimental def x = 2 @experimental class A { def f = x // ok because A is experimental } @experimental class B { def f = x // ok because A is experimental } @experimental object C { def f = x // ok because A is experimental } @experimental class D { def f = { object B { x // ok because A is experimental } } }
-
Annotations of an experimental definition are in experimental scopes. Examples:
Example 1
import scala.annotation.experimental @experimental class myExperimentalAnnot extends scala.annotation.Annotation @myExperimentalAnnot // error def test: Unit = () @experimental @myExperimentalAnnot def test: Unit = ()
-
Any code compiled using a Nightly or Snapshot version of the compiler is considered to be in an experimental scope. Can use the
-Yno-experimental
compiler flag to disable it and run as a proper release.
In any other situation, a reference to an experimental definition will cause a compilation error.
Experimental inheritance
All subclasses of an experimental class
or trait
must be marked as @experimental
even if they are in an experimental scope. Anonymous classes and SAMs of experimental classes are considered experimental.
We require explicit annotations to make sure we do not have completion or cycles issues with nested classes. This restriction could be relaxed in the future.
Experimental overriding
For an overriding member M
and overridden member O
, if O
is non-experimental then M
must be non-experimental.
This makes sure that we cannot have accidental binary incompatibilities such as the following change.
class A:
def f: Any = 1
class B extends A:
- @experimental def f: Int = 2
Test frameworks
Tests can be defined as experimental. Tests frameworks can execute tests using reflection even if they are in an experimental class, object or method. Examples:
Example 1
Test that touch experimental APIs can be written as follows
import scala.annotation.experimental
@experimental def x = 2
class MyTests {
/*@Test*/ def test1 = x // error
@experimental /*@Test*/ def test2 = x
}
@experimental
class MyExperimentalTests {
/*@Test*/ def test1 = x
/*@Test*/ def test2 = x
}