ОБНОВЛЕНИЕ: добавлена альтернатива в конце.
Вы сталкиваетесь с ограничениями сопоставления шаблонов, когда речь идет об общих типах, из-за стирания типов.
Однако не все потеряно. Мы можем положиться на ClassManifests для реализации универсального метода для преобразования ваших классов в целевой тип T (и другой аналогичный для преобразования в Meta[T]):
trait Meta[T] { this: T =>
type t = T
def metaManifest: ClassManifest[T]
def ~=(e: T): Boolean
}
abstract sealed class Base {
def as[T:ClassManifest]: Option[T] = {
if ( classManifest[T].erasure.isAssignableFrom( this.getClass ) ) Some( this.asInstanceOf[T] )
else None
}
def asMeta[T:ClassManifest]: Option[T with Meta[T]] = {
this match {
case meta: Meta[_] if classManifest[T] <:< meta.metaManifest => as[T].asInstanceOf[Option[T with Meta[T]]]
case _ => None
}
}
}
abstract sealed class A extends Base
case class Ide(s: String) extends A
case class MIde(s: String) extends A with Meta[A] {
val metaManifest = classManifest[A]
def ~=(e: A) = e match {
case e: Ide => true
case e: MIde => false
}
}
sealed abstract class B extends Base
case class Foo(s: String) extends B
Давайте проверим это в REPL:
scala> m.as[A]
res17: Option[A] = Some(MIde(x))
scala> m.asMeta[A]
res18: Option[A with Meta[A]] = Some(MIde(x))
scala> i.as[A]
res19: Option[A] = Some(Ide(i))
scala> i.asMeta[A]
res20: Option[A with Meta[A]] = None
scala> f.as[A]
res21: Option[A] = None
scala> f.asMeta[A]
res22: Option[A with Meta[A]] = None
Звучит неплохо. Теперь мы можем переписать наше сопоставление с образцом следующим образом:
(m, i) match {
case (x: Meta[T], y: T) if x ~= y => println("right")
case _ => println("wrong")
}
к этому:
(m.asMeta[T], i.as[T]) match {
case (Some(x), Some(y)) if x ~= y => println("right")
case _ => println("wrong")
}
Итак, ваш пример теперь будет выглядеть так:
object Test {
val m = MIde("x")
val i = Ide("i")
val f = Foo("f")
def test[T:ClassManifest]() {
(m.asMeta[T], i.as[T]) match {
case (Some(x), Some(y)) if x ~= y => println("right")
case _ => println("wrong")
}
// -> right
(m.asMeta[T], f.as[T]) match {
case (Some(x), Some(y)) if x ~= y => println("right")
case _ => println("wrong")
}
}
}
ОБНОВЛЕНИЕ: если установка явно metaManifest
каждый раз, когда вы смешиваете Meta
, невозможна, вы можете позволить scala автоматически выводить его, неявно передавая его в конструкторе Meta
s. Это означает, что Meta
теперь должен быть классом, и, как следствие, A
и B
(и все подобные типы, которые должны отображаться как параметр типа Meta
) теперь должны быть трейтами, так как вы не можете смешивать 2 класса. Таким образом, вы фактически меняете ограничение на другое. Выберите свой любимый. Вот так:
abstract sealed class Meta[T]( implicit val metaManifest: ClassManifest[T] ) { this: T =>
type t = T
def ~=(e: T): Boolean
}
trait Base {
def as[T:ClassManifest]: Option[T] = {
if ( classManifest[T].erasure.isAssignableFrom( this.getClass ) ) Some( this.asInstanceOf[T] )
else None
}
def asMeta[T:ClassManifest]: Option[T with Meta[T]] = {
this match {
case meta: Meta[_] if classManifest[T] != ClassManifest.Nothing && classManifest[T] <:< meta.metaManifest => as[T].asInstanceOf[Option[T with Meta[T]]]
case _ => None
}
}
}
trait A extends Base
case class Ide(s: String) extends A
case class MIde(s: String) extends Meta[A] with A {
def ~=(e: A) = e match {
case e: Ide => true
case e: MIde => false
}
}
trait B extends Base
case class Foo(s: String) extends B
object Test {
val m = MIde("x")
val i = Ide("i")
val f = Foo("f")
def test[T:ClassManifest]() {
(m.asMeta[T], i.as[T]) match {
case (Some(x), Some(y)) if x ~= y => println("right")
case _ => println("wrong")
}
(m.asMeta[T], f.as[T]) match {
case (Some(x), Some(y)) if x ~= y => println("right")
case _ => println("wrong")
}
}
}
Наконец, если ни одно из решений вам не подходит, вы можете попробовать другое: вместо того, чтобы смешивать Meta[T]
с T
, просто оберните его. Тогда Meta[T]
будет просто оболочкой для T
, и вы даже можете добавить неявное преобразование из Meta[T]
в его обернутое значение, чтобы экземпляр Meta[T]
можно было эффективно использовать как экземпляр T
почти прозрачно.
person
Régis Jean-Gilles
schedule
04.10.2012