1

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

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因此,对and/or使用类型构造函数参数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 }

?

4

2 回答 2

0

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

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]]在涉及变体类型参数时很容易破坏。

于 2014-04-24T09:21:32.423 回答
0

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

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

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

于 2014-04-24T13:44:11.567 回答