【问题标题】:Runtime type checks / pattern matching involving variable arity types涉及可变数量类型的运行时类型检查/模式匹配
【发布时间】:2014-04-24 08:35:12
【问题描述】:

我正在重新审视用于将异构基本类型分组在一起的通用包装器的问题。我正在使用类型成员,所以现在结构如下所示:

trait Outer[S] {
  type A1
  def inner: Inner[S] { type A = A1 }
}

trait Inner[S] {
  type A
  def peer: A
}

那么问题当然是测试特定对象,例如:

def test[S](o: Outer[S]): Option[Outer[S] { type A1 = String }] = o match {
  case os: Outer[S] { type A1 = String } => Some(os)
  case _ => None
}

由于类型擦除,这不起作用。问题是我必须为对等点抽象类型参数,也就是说,有(大多数)对等点也有一个类型参数[S],但其他人没有。因此,对Inner 和/或Outer 使用类型构造函数参数是不可行的。

廉价的解决方案是需要实际的子类:

trait StringOuter[S] extends Outer[S] { type A1 = String }

def test1[S](o: Outer[S]): Option[Outer[S] { type A1 = String }] = o match {
  case os: StringOuter[S] => Some(os)
  case _ => None
}

但我不喜欢这种解决方案,因为我会有很多不同的对等点,而且我不想为每个点创建专用的包装类。此外,如果我必须在每个子类中编写复制方法,复制这些对象也会变得很烦人。

所以我可能只剩下类标签了?如果我有以下两种类型参数不同的对等类型,这将如何解决:

trait Foo[S]
type Inner1[S] = Inner[S] { type A = Foo[S] }
type Inner2[S] = Inner[S] { type A = String }

?

【问题讨论】:

    标签: scala reflection pattern-matching higher-kinded-types


    【解决方案1】:

    如果我们暂时忘记模式匹配,可以在某种程度上使用普通的旧反射:

    import reflect.ClassTag
    
    trait Outer[S] {
      type A1
      def inner: Inner[S] { type A = A1 }
      def as[A](implicit tag: ClassTag[A]): Option[Outer[S] { type A1 = A }] =
        inner.peer match {
          case _: A => Some(this.asInstanceOf[Outer[S] { type A1 = A }])
          case _ => None
        }
    }
    
    trait Inner[S] {
      type A
      def peer: A
    }
    

    测试:

    trait Foo[S]    
    
    val x = new Outer[Unit] { 
      type A1 = String
      val inner = new Inner[Unit] {
        type A = String
        val peer = "foo"
      }
    }
    
    val y = new Outer[Unit] { 
      type A1 = Foo[Unit]
      val inner = new Inner[Unit] {
        type A = Foo[Unit]
        val peer = new Foo[Unit] {}
      }
    }
    
    val xs = x.as[String]
    val xi = x.as[Foo[Unit]]
    
    val ys = y.as[String]
    val yi = y.as[Foo[Unit]]
    

    现在唯一的问题是没有检查更高种类的类型:

    y.as[Foo[Nothing]]   // Some!
    

    另一个想法是更改我的设计以要求S 参数始终存在。那么

    trait Sys[S <: Sys[S]]
    
    trait Inner[S <: Sys[S], +Elem[~ <: Sys[~]]] {
      def peer: Elem[S]
      def as[A[~ <: Sys[~]]](implicit tag: ClassTag[A[S]]): Option[Inner[S, A]] =
        if (tag.unapply(peer).isDefined)
          Some(this.asInstanceOf[Inner[S, A]]) 
        else 
          None
    }
    
    type In[S <: Sys[S]] = Inner[S, Any]
    
    trait Foo[S <: Sys[S]] { def baz = 1234 }
    trait Bar[S <: Sys[S]]
    
    trait I extends Sys[I]
    
    val i: In[I] = new Inner[I, Foo] { val peer = new Foo[I] {} }
    val j: In[I] = new Inner[I, Bar] { val peer = new Bar[I] {} }
    
    assert(i.as[Foo].isDefined)
    assert(i.as[Bar].isEmpty  )
    assert(j.as[Foo].isEmpty  )
    assert(j.as[Bar].isDefined)
    

    或者上一个版本改回使用类型成员:

    trait Inner[S <: Sys[S]] {
      type Elem
    
      def peer: Elem
      def as[A[~ <: Sys[~]]](implicit tag: ClassTag[A[S]]): Option[InnerT[S, A]] =
        if (tag.unapply(peer).isDefined) 
          Some(this.asInstanceOf[InnerT[S, A]])
        else
          None
    }
    
    type InnerT[S <: Sys[S], A[~ <: Sys[~]]] = Inner[S] { type Elem = A[S] }
    
    
    val i: Inner[I] = new Inner[I] { type Elem = Foo[I]; val peer = new Foo[I] {} }
    val j: Inner[I] = new Inner[I] { type Elem = Bar[I]; val peer = new Bar[I] {} }
    
    assert(i.as[Foo].isDefined)
    assert(i.as[Bar].isEmpty  )
    assert(j.as[Foo].isEmpty  )
    assert(j.as[Bar].isDefined)
    
    val ix: InnerT[I, Foo] = i.as[Foo].get
    ix.peer.baz
    

    ...这可能有利于隐式解析,例如 Serializer[Inner[S]],当涉及变体类型参数时,它很容易被破坏。

    【讨论】:

      【解决方案2】:

      感谢类型擦除,在摆弄了几个小时之后,这是唯一可行的解​​决方案:

      trait StringOuter[S] extends Outer[S] { type A1 = String }
      

      尝试其他任何事情都是浪费精力。坚持使用平面类和不变类型。忘掉协变类型和模式匹配的场景吧。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-07-18
        • 2019-08-02
        • 1970-01-01
        • 2019-08-10
        • 2021-01-15
        • 2022-10-13
        • 1970-01-01
        相关资源
        最近更新 更多