【问题标题】:Scala compiler infers Nothing for generic argumentsScala 编译器为泛型参数推断 Nothing
【发布时间】:2014-09-20 15:00:02
【问题描述】:

我正在尝试一些我以前在不同情况下以不同形状看到的东西: 使用filterById(id: Id)扩展scala的查询扩展

这是我尝试过的:

trait TableWithId { self: Profile =>

    import profile.simple._

    trait HasId[Id] { self: Table[_] =>
      def id: Column[Id]
    }

    implicit class HasIdQueryExt[Id: BaseColumnType, U]
    (query: Query[Table[U] with HasId[Id], U]) {
      def filterById(id: Id)(implicit s: Session) = query.filter(_.id === id)
      def insertReturnId(m: U)(implicit s: Session): Id = query.returning(query.map(_.id)) += m
    }
}

这很好用,没有真正的魔法。但是因为对 Table 类型没有类型约束,所以我应用 filterById 的任何查询都会失去它的特异性(现在是通用的Table with HasId[Id]),并且我无法再访问它的列(_.id 当然除外) .

我不知道如何键入此隐式转换,以防止这种情况发生。可能吗?以下“幼稚”的解决方案不起作用,因为 Scala 现在为 Id 类型推断出 Nothing:

implicit class HasIdQueryExt[Id: BaseColumnType, U, T <: Table[U] with HasId[Id]]
(query: Query[T, U]) { ... }

我觉得有点奇怪,突然 Id 类型被推断为 Nothing。如何提示编译器在哪里查找该 Id 类型?

【问题讨论】:

  • 我对 Slick 不熟悉,但我的猜测是,您的解决方案需要一个通用参数,该参数采用您的表的实际类型......类似于 'HasId[Id, T <: hasid t href="http://java.dzone.com/articles/duck-typing-scala-structural" rel="nofollow" target="_blank">java.dzone.com/articles/duck-typing-scala-structural 这将使用反射,但仍为您提供静态安全。
  • 我看不出这与我上次写下的方法有何不同。那些不是等价的吗?而且我认为结构类型在这种情况下没有帮助。问题保留了它的结构。

标签: scala generics implicit-conversion type-inference slick


【解决方案1】:

这是我对类似问题的解决方案。我确实使用了特定类型的 id。:

trait GenericComponent { this: Profile =>
  import profile.simple._

  abstract class TableWithId[A](tag:Tag, name:String) extends Table[A](tag:Tag, name) {
    def id = column[Option[UUID]]("id", O.PrimaryKey)
  }

  abstract class genericTable[T <: Table[A] , A] {
    val table: TableQuery[T]

    /**
     * generic methods
     */

    def insert(entry: A)(implicit session:Session): A = session.withTransaction { 
      table += entry
      entry
    }

    def insertAll(entries: List[A])(implicit session:Session) = session.withTransaction { table.insertAll(entries:_*) }

    def all: List[A] = database.withSession { implicit session =>
      table.list.map(_.asInstanceOf[A])
    }
  }

  /**
   * generic queries for any table which has id:Option[UUID]
   */
  abstract class genericTableWithId[T <: TableWithId[A], A <:ObjectWithId ] extends genericTable[T, A] {

    def forIds(ids:List[UUID]): List[A] = database.withSession { implicit session =>
      ids match {
        case Nil => Nil
        case x::xs =>table.filter(_.id inSet(ids)).list.map(_.asInstanceOf[A])
      }
    }

    def forId(id:UUID):Option[A] = database.withSession { implicit session =>table.filter(_.id === id).firstOption }

  }
}

然后是你的具体组件:

case class SomeObjectRecord(
  override val id:Option[UUID] = None,
  name:String) extends ObjectWithId(id){
  // your function definitions there
} 

trait SomeObjectComponent extends GenericComponent { this: Profile =>
  import profile.simple._

  class SomeObjects(tag: Tag) extends TableWithId[SomeObjectRecord](tag, "some_objects") {
    def name = column[String]("name", O.NotNull)

    def * = (id, name) <> (SomeObjectRecord.tupled, SomeObjectRecord.unapply)
    def nameIdx = index("u_name", (name), unique = true)
  }

  object someobjects extends genericTableWithId[SomeObjects, SomeObjectRecord] {
    val table = TableQuery[Units]

   // your additional methods there; you get insert and insertAll from the parent    
  }
}

【讨论】:

  • 谢谢。但这当然不能回答我的问题;它可以解决问题。因为您的建议是:删除 Id 泛型参数。此外,我更喜欢 QueryExtensions 而不是带有方法的伴随对象,因为查询上的扩展可以在查询给出特定结果形状的任何地方重复使用,而您的附加方法只能在原样中使用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-10-16
  • 1970-01-01
  • 1970-01-01
  • 2014-02-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多