【问题标题】:How to accept only a specific subtype of existential type?如何只接受存在类型的特定子类型?
【发布时间】:2022-10-04 20:42:35
【问题描述】:

考虑以下实现草图:

sealed trait Type

object Type {
  case object Type1 extends Type
  case object Type2 extends Type
}

sealed trait Data {
  type T <: Type
}
object Data {
  type Aux[TT] = Data {
    type T = TT
  }

  case class Data1(i: Int) extends Data {
    type T = Type1.type
  }

  case class Data2(s: String) extends Data {
    type T = Type2.type
  }
}

case class Main(
  //lots of other fields
  data: Data.Aux[T] forSome { type T <: Type}
)

// This method is supposed to accept the only Main's
// that have data of type Data.Aux[Type2.type]
def handleMainType2(
  main: Main
): Unit = ???

问题:

case class 包含一个存在类型的字段,就可以实现一个接受存在类型的唯一分支的方法。

也许无形在这里会有所帮助?

【问题讨论】:

    标签: scala generics shapeless existential-type typesafe


    【解决方案1】:

    首先Data.Aux[T] forSome { type T &lt;: Type}可以写成Data.Aux[_]也就是Data

    implicitly[(Data.Aux[T] forSome { type T <: Type}) =:= Data] // compiles
    implicitly[Data =:= (Data.Aux[T] forSome { type T <: Type})] // compiles
    

    如果你把println 放在handleMainType2 里面

    import scala.reflect.runtime.universe.{Type, TypeTag, typeOf}
    def getType[A: TypeTag](a: A): Type = typeOf[A]
    
    def handleMainType2(main: Main): Unit = 
      println(getType(main.data) + "=" + showRaw(getType(main.data)))
    

    然后

    handleMainType2(Main(Data1(1)))
    handleMainType2(Main(Data2("a")))
    

    将打印以下内容之一 (取决于您如何定义 Main 参数的类型:Data.Aux[T] forSome { type T &lt;: Type}Data.Aux[_]Data

    App.Data{type T = T}=RefinedType(List(TypeRef(ThisType(App), App.Data, List())), Scope(TypeName("T")))
    App.Data{type T = _$1}=RefinedType(List(TypeRef(ThisType(App), App.Data, List())), Scope(TypeName("T")))
    App.Data=TypeRef(ThisType(App), App.Data, List())
    

    两次。所以内部方法 handleMainType2 main.data 的类型只有 DataData1/Data2 无法按类型区分。可区分的是运行时类:

    def handleMainType2(main: Main): Unit =
      println(main.data.getClass)
    
    //class App$Data$Data1
    //class App$Data$Data2
    

    所以你可以定义

    def handleMainType2(main: Main): Unit =
      assert(main.data.getClass.isAssignableFrom(classOf[Data2]))
    

    与运行时行为。

    如果您想要编译时行为,那么您可以尝试将 handleMainType2 设为宏并在宏中使用运行时反射

    // in a different subproject
    import scala.language.experimental.macros
    import scala.reflect.macros.blackbox
    
    def handleMainType2(main: Main): Unit = macro handleMainType2Impl
    
    def handleMainType2Impl(c: blackbox.Context)(main: c.Tree): c.Tree = {
      import c.universe._
      val clazz = c.eval(c.Expr[Main](c.untypecheck(main))).data.getClass
      if (!clazz.isAssignableFrom(classOf[Data2]))
        c.abort(c.enclosingPosition, s"${clazz.getName} <:!< Data2")
      else q"()"
    }
    
    handleMainType2(Main(Data1(1))) // doesn't compile
    handleMainType2(Main(Data2("a"))) // compiles
    

    如果您不想将 handleMainType2 设为宏本身,您甚至可以将宏设为隐式。

    trait IsData2[D <: Data with Singleton]
    
    object IsData2 {
      implicit def mkIsData2[D <: Data with Singleton]: IsData2[D] = macro mkIsData2Impl[D]
    
      def mkIsData2Impl[D <: Data with Singleton : c.WeakTypeTag](c: whitebox.Context): c.Tree = {
        import c.universe._
    
        val clazz = c.eval(c.Expr[ValueOf[D]](c.untypecheck(
          c.inferImplicitValue(weakTypeOf[ValueOf[D]], silent = false)
        ))).value.getClass
    
        if(!clazz.isAssignableFrom(classOf[Data2])) {
          c.abort(c.enclosingPosition, s"${weakTypeOf[D]} <:!< Data2")
        } else q"new IsData2[${weakTypeOf[D]}] {}"
      }
    
    object App {
      val m1: Main = Main(Data1(1))
      val m2: Main = Main(Data2("a"))
    }
    
    def handleMainType2(main: Main)(implicit ev: IsData2[main.data.type]) = ()
    
    handleMainType2(App.m1) // doesn't compile
    handleMainType2(App.m2) // compiles
    

    请注意handleMainType2(Main(Data2("a"))) 甚至

    val m2: Main = Main(Data2("a"))
    handleMainType2(m2) 
    

    不管用。

    【讨论】:

      猜你喜欢
      • 2015-03-28
      • 1970-01-01
      • 1970-01-01
      • 2020-05-20
      • 2012-05-21
      • 1970-01-01
      • 1970-01-01
      • 2021-05-26
      • 2013-11-08
      相关资源
      最近更新 更多