복붙노트

[SCALA] 나는 스칼라에 밀봉 부모로부터 파생 케이스의 모든 개체의 컴파일 시간 목록을받을 수 있습니까?

SCALA

나는 스칼라에 밀봉 부모로부터 파생 케이스의 모든 개체의 컴파일 시간 목록을받을 수 있습니까?

SO에 여러 번 언급 한 바와 같이 당신이 철저하게 봉인 클래스에서 파생 유형을 모두 나열하지 않는 경우, 스칼라 경기는 경고합니다.

내가 원하는 특정 부모로부터 파생 경우 객체의 Iterable을 생성 컴파일 시간이다. 또한, 나는 컴파일러가 나는 약간의 Iterable에서 필요한 모든 유형이없는 말해 만드는 방법에 만족하실 것입니다. 나는 런타임, 반사 기반의 접근 방식을 원하지 않는다.

두 번째 방법의 예를 들어, 나는 표시된 경우 다음과 같은 거친 코드는 컴파일 오류를 생성해야하고 싶습니다.

sealed trait Parent
case object A extends Parent
case object B extends Parent
case object C extends Parent

// I want a compiler error here because C is not included in the Seq()
val m = Seq(A, B).map(somethingUseful)

그것은 불가능 말해 의해 답변을 주시기 바랍니다. 그냥 경기를 결정하는 비 철저한 때 컴파일러는 본질적으로 같은 일을해야하기 때문에이 어떤 수준에서 가능해야한다처럼 보인다.

그것에 대해 다른 방법을 생각, 나는 개체를 경우에 적용 제외 Enumeration.values ​​같은 것을 () 메소드를 취할 것입니다. 물론, 나는 부모의 동반자 개체에 대한 값을 수동으로 유지 목록 위의 코드와 유사한 무언가를 추가 할 수 있지만, 불필요하게 보인다 오류가 발생하기 쉬운 컴파일러가 나를 위해 그렇게 할 수있을 때.

// Manually maintained list of values
object Parent { 
    val values = Seq(A, B, C)
}

해결법

  1. ==============================

    1.최신 정보. 2.10.0-M7 때문에 우리는 공개 API의 일부로서이 답변에 언급 된 방법을 노출하고 있습니다. isSealed는 ClassSymbol.isSealed 및 sealedDescendants이 ClassSymbol.knownDirectSubclasses입니다.

    최신 정보. 2.10.0-M7 때문에 우리는 공개 API의 일부로서이 답변에 언급 된 방법을 노출하고 있습니다. isSealed는 ClassSymbol.isSealed 및 sealedDescendants이 ClassSymbol.knownDirectSubclasses입니다.

    이 질문에 대한 답변 될 수 없습니다.

    그러나, 당신은 더 Enumeration.values ​​()과 같은 정착 할 의향이 있고 2.10의 최근 이정표를 사용하고, 당신은 못생긴 캐스팅 - 투 - 내부 API를 사업에 대한 깨끗이하고자하는 경우, 다음을 쓸 수 있습니다 :

    import scala.reflect.runtime.universe._
    
    def sealedDescendants[Root: TypeTag]: Option[Set[Symbol]] = {
      val symbol = typeOf[Root].typeSymbol
      val internal = symbol.asInstanceOf[scala.reflect.internal.Symbols#Symbol]
      if (internal.isSealed)
        Some(internal.sealedDescendants.map(_.asInstanceOf[Symbol]) - symbol)
      else None
    }
    

    지금 당신은이 같은 계층 구조를 가지고있는 경우 :

    object Test {
      sealed trait Parent
      case object A extends Parent
      case object B extends Parent
      case object C extends Parent
    }
    

    이 같은 밀폐 된 유형 계층 구조의 멤버에 대한 형식 기호를 얻을 수 있습니다 :

    scala> sealedDescendants[Test.Parent] getOrElse Set.empty
    res1: Set[reflect.runtime.universe.Symbol] = Set(object A, object B, object C)
    

    그것은 끔찍한,하지만 난 당신이 컴파일러 플러그인을 작성하지 않고 당신이 실제로 원하는 것을 얻을 거라고 생각하지 않습니다.

  2. ==============================

    2.여기 2.10.0-M6에 매크로를 사용하여 동작하는 예제입니다 :

    여기 2.10.0-M6에 매크로를 사용하여 동작하는 예제입니다 :

    (업데이트 : 2.10.0-M7에서이 예제가 제대로 작동되게하려면, 당신은 c.AbsTypeTag와 c.TypeTag를 교체해야; 2.10.0-RC1에서이 예제가 제대로 작동되게하려면, c.AbsTypeTag는 c.WeakTypeTag로 교체해야 )

    import scala.reflect.makro.Context
    
    object SealednessMacros {
      def exhaustive[P](ps: Seq[P]): Seq[P] = macro exhaustive_impl[P]
    
      def exhaustive_impl[P: c.TypeTag](c: Context)(ps: c.Expr[Seq[P]]) = {
        import c.universe._
    
        val symbol = typeOf[P].typeSymbol
    
        val seen = ps.tree match {
          case Apply(_, xs) => xs.map {
            case Select(_, name) => symbol.owner.typeSignature.member(name)
            case _ => throw new Exception("Can't check this expression!")
          }
          case _ => throw new Exception("Can't check this expression!")
        }
    
        val internal = symbol.asInstanceOf[scala.reflect.internal.Symbols#Symbol]    
        if (!internal.isSealed) throw new Exception("This isn't a sealed type.")
    
        val descendants = internal.sealedDescendants.map(_.asInstanceOf[Symbol])
    
        val objs = (descendants - symbol).map(
          s => s.owner.typeSignature.member(s.name.toTermName)
        )
    
        if (seen.toSet == objs) ps else throw new Exception("Not exhaustive!")
      }
    }
    

    이것은 분명히 그것을, 그것은 여전히 ​​불쾌한 캐스팅이 필요하지만 (예를 들어, 당신은 계층 구조의 개체를 가지고 있고, 그것은 ::의 B : C : 무기 호에 실패 할 거라고 가정) 매우 강력하지 않다 빠른 개념 증명으로 작동합니다.

    먼저 우리는 활성화 매크로이 파일을 컴파일 :

    scalac의 -language : experimental.macros SealednessMacros.scala

    이제 우리는이 같은 파일을 컴파일하려고하면 :

    object MyADT {
      sealed trait Parent
      case object A extends Parent
      case object B extends Parent
      case object C extends Parent
    }
    
    object Test extends App {
      import MyADT._
      import SealednessMacros._
    
      exhaustive[Parent](Seq(A, B, C))
      exhaustive[Parent](Seq(C, A, B))
      exhaustive[Parent](Seq(A, B))
    }
    

    우리는 누락 된 C와 서열에 컴파일 타임 오류가 발생합니다 :

    Test.scala:14: error: exception during macro expansion: 
    java.lang.Exception: Not exhaustive!
            at SealednessMacros$.exhaustive_impl(SealednessMacros.scala:29)
    
      exhaustive[Parent](Seq(A, B))
                        ^
    one error found
    

    우리가 부모를 나타내는 명시 적 형식 매개 변수와 컴파일러를 도울 필요가 있습니다.

  3. from https://stackoverflow.com/questions/12078366/can-i-get-a-compile-time-list-of-all-of-the-case-objects-which-derive-from-a-sea by cc-by-sa and MIT license