실험적 정의
실험적 정의 (Experimental Definitions)
@experimental 애너테이션은 이진·소스 호환성이 보장되지 않는 API를 정의할 수 있게 해 줘요. 이 애너테이션은 용어(term) 정의나 타입 정의에 붙일 수 있어요.
본문
실험적 정의에 대한 참조 (References to experimental definitions)
실험적 정의는 **실험적 스코프(experimental scope)**에서만 참조할 수 있어요. 실험적 스코프는 다음과 같이 정의돼요.
- 실험적인
def,val,var,given,type의 RHS는 실험적 스코프예요. 예시:
예시 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
예시 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()
예시 3
import scala.annotation.experimental
@experimental type E
type A = E // error type E is marked @experimental and therefore ...
@experimental type B = E
예시 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 ...
()
예시 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
()
- 실험적인
def,val,var,given,type의 시그니처(signature), 그리고class와trait의 생성자들은 실험적 스코프예요. 예시:
예시 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
) {}
- 실험적인
class,trait,object의 extends 절은 실험적 스코프예요. 예시:
예시 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
- 실험적인
class,trait,object의 **본문(body)**은 실험적 스코프예요. 예시:
예시 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
}
}
}
- 실험적 정의의 애너테이션들은 실험적 스코프에 있어요. 예시:
예시 1
import scala.annotation.experimental
@experimental class myExperimentalAnnot extends scala.annotation.Annotation
@myExperimentalAnnot // error
def test: Unit = ()
@experimental
@myExperimentalAnnot
def test: Unit = ()
- 실험적 언어 기능이 패키지 레벨에서 import되면, 모든 최상위 정의가
@experimental로 표시돼요.
그 외의 어떤 상황에서든 실험적 정의에 대한 참조는 컴파일 오류를 일으켜요.
실험적 오버라이딩 (Experimental overriding)
오버라이딩 멤버 M과 오버라이딩되는 멤버 O가 있을 때, O가 실험적이지 않다면 M도 실험적이지 않아야 해요.
이 규칙은 다음과 같은 변경으로 생길 수 있는 우발적인 이진 비호환성을 막아 줘요:
class A:
def f: Any = 1
class B extends A:
- @experimental def f: Int = 2
테스트 프레임워크 (Test frameworks)
테스트는 실험적으로 정의될 수 있어요. 테스트 프레임워크는 테스트가 실험적인 class, object, 메서드 안에 있더라도 리플렉션을 사용해 테스트를 실행할 수 있어요. 예시:
예시 1
실험적 API를 건드리는 테스트는 다음과 같이 쓸 수 있어요:
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
}
-experimental 컴파일러 플래그 (-experimental compiler flag)
이 플래그는 프로젝트에서 어떤 실험적 언어 기능이든 사용할 수 있게 해 줘요. 모든 최상위 정의에 @experimental 애너테이션을 추가하는 방식으로 동작하죠. 그래서 이에 의존하는 프로젝트들도 실험적이어야 해요.