0

I have this type 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
}

with this type class instance:

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

But when trying to use the type class instance with something like dbStringOps.isValidContent(newContent) where newcontent is a string, i get a type mismatch:

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

I can get it to work by converting R from an abstract type member to a type parameter, but that is ugly since R is already determined when i'm writing the implementation of the type class.

7

add type annotations on that implicit val.

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

or

receive implicit parameter using this signature.

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

can also write like this.

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

def f()(implicit db: AUX[DbString, String]) = ...
  • nice, I forgot about the structural trick. – GClaramunt Apr 21 at 4:16
  • When are those type annotations necessary? Is it always when using abstract type members in type classes? And what is: type AUX[A, T] = DbValueOps[A] { type R = T } called? I don't exactly understand what that does. – Frederik Baetens Apr 21 at 14:25
  • How do i now make classes with methods generic over dbValueOps? I'm trying to do so with a class definition like:case class Column[R, T <: DbValue[R]] private (...)(implicit ops: DbValueOps){ And a method like: ` def createDbValue(): T = { ops.fromString("hi").get }` But i get the error: found: Column.this.ops.T, required: T – Frederik Baetens Apr 21 at 19:45
  • I ended up ditching the abstract type members in dbValueOps (still have one in dbValue though) because i want to use them in a generic way in other classes, and i don't know how to get the information of what the return type of the functions in dbValueOps are into those other classes without using type parameters – Frederik Baetens Apr 21 at 20:14
  • I'm doing so like this: case class Column[R, T <: DbValue[R, T]] private (...)(implicit ops: DbValueOps[R, T]) { This did kind of accomplish my goal of forcing there to be an associated dbValueOps typeclass instance, but i don't like that i have to explictly pass the typeclass object. – Frederik Baetens Apr 21 at 20:40
4

恵砂川 answer solves your problem perfectly, but unless you really want to center your design on DbValues, I will suggest to center your implicit on the wrapped value (String on this case), as you don't need to provide the unification of R with 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)
  }

Adding a type parameter to DbValue can be a little more verbose but it prevents you to define things like DbValueOps[DbString] { type R = Int } that probably is NOT what are you looking for.

  • I have accepted 恵砂川 's answer, because they directly answered my question, but I will be going with your design! – Frederik Baetens Apr 21 at 13:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.