Scala Seq - 仅接受相同子类型的元素

2024-04-20

假设我有一个如下所示的类型层次结构:

trait Color
case class Red(r: String) extends Color
case class Green(g: String) extends Color

是否可以创建一个接受Seq[Color]包含所有元素Red,或者全部Green,但不能两者兼而有之?

例如在下面的代码中:

def process[T](colors: Seq[T]) = colors.size

process(Seq(Red("a"), Green("g")))

什么应该[T]这样上面的内容就不会进行类型检查吗?

Edit

最初的问题如下:我正在尝试为嵌套查询设计一个 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] 上进行模式匹配。


试试这个(部分基于迈尔斯·萨宾 (Miles Sabin) 的这篇博文 https://milessabin.com/blog/2011/06/09/scala-union-types-curry-howard/):

// 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注解)。

基本思想是我们为基类创建不明确的隐式,但不为子类创建。由于隐式解析规则,这将仅针对子类进行编译,而不针对基类进行编译。

清理后的版本:

@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)
  }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Scala Seq - 仅接受相同子类型的元素 的相关文章

随机推荐