실험적 정의

실험적 정의 (Experimental Definitions)

@experimental 애너테이션은 이진·소스 호환성이 보장되지 않는 API를 정의할 수 있게 해 줘요. 이 애너테이션은 용어(term) 정의나 타입 정의에 붙일 수 있어요.

출처: Scala 3 Reference

본문

실험적 정의에 대한 참조 (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), 그리고 classtrait의 생성자들은 실험적 스코프예요. 예시:

예시 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, objectextends 절은 실험적 스코프예요. 예시:

예시 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 애너테이션을 추가하는 방식으로 동작하죠. 그래서 이에 의존하는 프로젝트들도 실험적이어야 해요.