Scala Seq - 只接受相同子类型的元素
Scala Seq - accept only elements of the same subtype
假设我有如下类型层次结构:
trait Color
case class Red(r: String) extends Color
case class Green(g: String) extends Color
是否可以创建一个接受 Seq[Color]
的方法,该 Seq[Color]
包含全部 Red
或全部 Green
的元素,但不能同时包含两者?
例如在下面的代码中:
def process[T](colors: Seq[T]) = colors.size
process(Seq(Red("a"), Green("g")))
[T]
应该怎么做才能使上面的代码不进行类型检查?
编辑
最初的问题如下:我正在尝试为嵌套查询设计 JSON API。我想出了以下设计:
trait QueryUnit
case class SimpleQuery(query: String, metadata: Metadata)
case class ComplexQuery(Map[String, Seq[SimpleQuery])
case class API(
query: Map[String, Seq[QueryUnit]]
)
Map 的元素将是连词 (AND),而 Seq 的元素将是析取 (OR)。我不想将 Simple 与 ComplexQueries 混合使用,我打算在 Seq[QueryUnit] 上进行模式匹配。
试试这个(部分基于 this blog post from Miles Sabin):
// Your domain
trait QueryUnit
case class SimpleQuery(query: String, metadata: AnyRef) extends QueryUnit
case class ComplexQuery(map: Map[String, Seq[SimpleQuery]]) extends QueryUnit
// End of your domain
// Something which has type parameter, so we can add QueryUnit, ...
trait WrongArg[T]
// Create ambiguous implicits for QueryUnit
implicit def v0: WrongArg[QueryUnit] = ???
implicit def v2: WrongArg[QueryUnit] = ???
// And valid values for the concrete subclasses
implicit val simpleQWrongArg: WrongArg[SimpleQuery] = new WrongArg[SimpleQuery] {}
implicit val complexQWrongArg: WrongArg[ComplexQuery] = new WrongArg[ComplexQuery] {}
case class API[QU <: QueryUnit](
query: Map[String, Seq[QU]]
// Require an evidence that we are getting the correct type
)(implicit w: WrongArg[QU]) {
}
API/*[SimpleQuery]*/(Map("" -> Seq(SimpleQuery("", ""))))
API/*[ComplexQuery]*/(Map("" -> Seq(ComplexQuery(Map.empty))))
// Fails to compile because of ambiguous implicits
//API(Map("s" -> Seq(SimpleQuery("", "")), "c" -> Seq(ComplexQuery(Map.empty))))
不理想(名字错误,没有@implicitNotFound
注释)。
基本思想是我们为基 class 创建模糊的隐式,但不为子 class es。由于隐式解析规则,这将仅针对子classes 进行编译,而不会针对基class.
进行编译
清理后的版本:
@annotation.implicitNotFound("Base QueryUnit is not supported, you cannot mix SimpleQuery and ComplexQuery")
trait Handler[T] extends (T => Unit)
object API {
implicit def baseQueryUnitIsNotSupported0: Handler[QueryUnit] = ???
implicit def baseQueryUnitIsNotSupported1: Handler[QueryUnit] = ???
implicit val simpleQWrongArg: Handler[SimpleQuery] = new Handler[SimpleQuery] {
override def apply(s: SimpleQuery): Unit = {}
}
implicit val complexQWrongArg: Handler[ComplexQuery] = new Handler[ComplexQuery] {
override def apply(s: ComplexQuery): Unit = {}
}
}
case class API[QU <: QueryUnit](query: Map[String, Seq[QU]])(
implicit handler: Handler[QU]) {
// Do something with handler for each input
}
// Usage
import API._
import scala.annotation.implicitNotFound
API/*[SimpleQuery]*/(Map("" -> Seq(SimpleQuery("", ""))))
API/*[ComplexQuery]*/(Map("" -> Seq(ComplexQuery(Map.empty))))
// Error:(56, 71) Base QueryUnit is not supported, you cannot mix SimpleQuery and ComplexQuery
//API(Map("s" -> Seq(SimpleQuery("", "")), "c" -> Seq(ComplexQuery(Map.empty))))
或者使用类型边界和 implicitly
:
case class API[QU <: QueryUnit: Handler](query: Map[String, Seq[QU]]) {
def doSomething: Unit = for {(_, vs) <- query
v <- vs} {
val handler: Handler[QU] = implicitly[Handler[QU]]
handler(v)
}
}
假设我有如下类型层次结构:
trait Color
case class Red(r: String) extends Color
case class Green(g: String) extends Color
是否可以创建一个接受 Seq[Color]
的方法,该 Seq[Color]
包含全部 Red
或全部 Green
的元素,但不能同时包含两者?
例如在下面的代码中:
def process[T](colors: Seq[T]) = colors.size
process(Seq(Red("a"), Green("g")))
[T]
应该怎么做才能使上面的代码不进行类型检查?
编辑
最初的问题如下:我正在尝试为嵌套查询设计 JSON API。我想出了以下设计:
trait QueryUnit
case class SimpleQuery(query: String, metadata: Metadata)
case class ComplexQuery(Map[String, Seq[SimpleQuery])
case class API(
query: Map[String, Seq[QueryUnit]]
)
Map 的元素将是连词 (AND),而 Seq 的元素将是析取 (OR)。我不想将 Simple 与 ComplexQueries 混合使用,我打算在 Seq[QueryUnit] 上进行模式匹配。
试试这个(部分基于 this blog post from Miles Sabin):
// Your domain
trait QueryUnit
case class SimpleQuery(query: String, metadata: AnyRef) extends QueryUnit
case class ComplexQuery(map: Map[String, Seq[SimpleQuery]]) extends QueryUnit
// End of your domain
// Something which has type parameter, so we can add QueryUnit, ...
trait WrongArg[T]
// Create ambiguous implicits for QueryUnit
implicit def v0: WrongArg[QueryUnit] = ???
implicit def v2: WrongArg[QueryUnit] = ???
// And valid values for the concrete subclasses
implicit val simpleQWrongArg: WrongArg[SimpleQuery] = new WrongArg[SimpleQuery] {}
implicit val complexQWrongArg: WrongArg[ComplexQuery] = new WrongArg[ComplexQuery] {}
case class API[QU <: QueryUnit](
query: Map[String, Seq[QU]]
// Require an evidence that we are getting the correct type
)(implicit w: WrongArg[QU]) {
}
API/*[SimpleQuery]*/(Map("" -> Seq(SimpleQuery("", ""))))
API/*[ComplexQuery]*/(Map("" -> Seq(ComplexQuery(Map.empty))))
// Fails to compile because of ambiguous implicits
//API(Map("s" -> Seq(SimpleQuery("", "")), "c" -> Seq(ComplexQuery(Map.empty))))
不理想(名字错误,没有@implicitNotFound
注释)。
基本思想是我们为基 class 创建模糊的隐式,但不为子 class es。由于隐式解析规则,这将仅针对子classes 进行编译,而不会针对基class.
进行编译清理后的版本:
@annotation.implicitNotFound("Base QueryUnit is not supported, you cannot mix SimpleQuery and ComplexQuery")
trait Handler[T] extends (T => Unit)
object API {
implicit def baseQueryUnitIsNotSupported0: Handler[QueryUnit] = ???
implicit def baseQueryUnitIsNotSupported1: Handler[QueryUnit] = ???
implicit val simpleQWrongArg: Handler[SimpleQuery] = new Handler[SimpleQuery] {
override def apply(s: SimpleQuery): Unit = {}
}
implicit val complexQWrongArg: Handler[ComplexQuery] = new Handler[ComplexQuery] {
override def apply(s: ComplexQuery): Unit = {}
}
}
case class API[QU <: QueryUnit](query: Map[String, Seq[QU]])(
implicit handler: Handler[QU]) {
// Do something with handler for each input
}
// Usage
import API._
import scala.annotation.implicitNotFound
API/*[SimpleQuery]*/(Map("" -> Seq(SimpleQuery("", ""))))
API/*[ComplexQuery]*/(Map("" -> Seq(ComplexQuery(Map.empty))))
// Error:(56, 71) Base QueryUnit is not supported, you cannot mix SimpleQuery and ComplexQuery
//API(Map("s" -> Seq(SimpleQuery("", "")), "c" -> Seq(ComplexQuery(Map.empty))))
或者使用类型边界和 implicitly
:
case class API[QU <: QueryUnit: Handler](query: Map[String, Seq[QU]]) {
def doSomething: Unit = for {(_, vs) <- query
v <- vs} {
val handler: Handler[QU] = implicitly[Handler[QU]]
handler(v)
}
}