Reputation: 871
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.
Upvotes: 0
Views: 664
Reputation: 3158
恵砂川 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.
Upvotes: 4
Reputation: 218
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]) = ...
Upvotes: 7