scala 类型 class 可以有抽象类型成员吗?

Can a scala type class have abstract type members?

我有这种类型class:

sealed trait DbValueOps[T <: DbValue] {
  type R
  def apply(newContent: R): Option[T]
  def fromString(newContent: String): Option[T]
  def isValidContent(newContent: R): Boolean
}

此类型 class 实例:

package object DbOps {
  implicit val dbStringOps: DbValueOps[DbString] = new DbValueOps[DbString] {
    type R = String
    def apply(newContent: String): Option[DbString] =
      isValidContent(newContent) match {
        case true => Some(new DbString(newContent))
        case false => None
      }
    def fromString(newContent: String): Option[DbString] = this(newContent)
    def isValidContent(newContent: String): Boolean = !newContent.isEmpty
  }
}

但是当尝试将类型 class 实例与类似 dbStringOps.isValidContent(newContent) 的内容一起使用时,其中 newcontent 是一个字符串,我得到类型不匹配:

found   : newContent.type (with underlying type String)
required: database.DbOps.dbStringOps.R

我可以通过将 R 从抽象类型成员转换为类型参数来使其工作,但这很难看,因为在我编写类型 class 的实现时 R 已经确定。

implicit val 上添加类型注释。

implicit val dbStringOps: DbValueOps[DbString] { type R = String } = ...

使用此签名接收隐式参数。

def f()(implicit db: DbValueOps[DbString] { type R = String }) = ...

也可以这样写

type AUX[A, T] = DbValueOps[A] { type R = T }

def f()(implicit db: AUX[DbString, String]) = ...

恵砂川答案完美地解决了你的问题,但除非你真的想将你的设计集中在 DbValues 上,否则我建议你将你的隐式集中在包装值(本例中为 String)上,因为你不需要提供 R 与 String 的统一。

  trait DbValue[T]
  case class DbString(s:String) extends DbValue[String]


  sealed trait DbOps[R]{
    type T <: DbValue[R]
    def apply(newContent: R): Option[T]
    def fromString(newContent: String): Option[T]
    def isValidContent(newContent: R): Boolean
  }

  object DbOps {
    val dbStringOps: DbOps[String] = new DbOps[String] {
      type T = DbString
      def apply(newContent: String): Option[DbString] =
        isValidContent(newContent) match {
          case true => Some(new DbString(newContent))
          case false => None
        }
      def fromString(newContent: String): Option[DbString] = this(newContent)
      def isValidContent(newContent: String): Boolean = !newContent.isEmpty
    }

    val newContent = "hello"
    dbStringOps.isValidContent(newContent)
  }

向 DbValue 添加类型参数可能有点冗长,但它会阻止您定义诸如 DbValueOps[DbString] { type R = Int } 之类的东西,这可能不是您要找的东西。