【问题标题】:Scala Seq - accept only elements of the same subtypeScala Seq - 只接受相同子类型的元素
【发布时间】:2017-12-03 00:57:04
【问题描述】:

假设我的类型层次结构如下:

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] 应该是什么才能使上述内容不进行类型检查?

编辑

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

【问题讨论】:

  • 对我来说听起来像XY problem。你能详细说明一下这个问题吗?
  • 当然,很公平 :) 我会更新关于我原来的问题的更多信息。
  • 已更新原始问题
  • 您是否考虑过在process 内部的QueryUnit 实例上进行模式匹配?
  • 然后你必须做出决定,因为每种方法都有权衡。要么采用Seq[Query] 和模式匹配,它为您提供递归遍历复杂查询的灵活性,或者使用更简单的方法并在单独的方法中获取每个实例。至少这是我现在从你的问题中看到的。

标签: scala scala-collections type-systems


【解决方案1】:

试试这个(部分基于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注解)。

基本思想是我们为基类创建模糊隐含,但不为子类创建。由于隐式解析规则,这只会​​为子类编译,而不是为基类编译。

清理后的版本:

@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)
  }
}

【讨论】:

  • 在无形中可能也有一些东西,但这并不难做到。
  • 我认为这使得 API 变得不平凡,OP 想要一个“简单的解决方案”,我认为这并不简单。
  • 在问题中查看我对 OP 的 cmets:" a) 我想要一个更简单的 API"。您不必去宏观层面使事情复杂化:) 如果 OP 发现与底层类型的 ADT 匹配的模式,我几乎不认为这会成为一种更简单的方法:)
  • (我添加了一个清理版本。我希望通过这种方式可以更清楚地了解它不仅可以用于限制输入,还可以根据它们的类型处理它们。)
  • 接受,因为它回答了原始问题和理论问题。但是,我认为它主要是类型系统上的一种解决方法,如果不是绝对必要的话,不打算在现实生活中使用它。我可能会做与@YuvalItzchakov 讨论过的更简单的模式匹配版本,并且可能会在应用程序端添加一些检查。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-04
  • 1970-01-01
  • 2022-10-04
  • 2020-01-05
相关资源
最近更新 更多