Let us have a type class which depending on the type defines some more types to work with:
trait Container[T] {
type Elem
def get(c: T, i: Int): Elem
def set(c: String, i: Int, v: Elem): T
}
implicit object StringContainer extends Container[String] {
type Elem = Char
def get(c: String, i: Int) = c(i)
def set(c: String, i: Int, v: Char) = c.patch(i, Seq(v), 1)
}
val ops = implicitly[Container[String]]
ops.set("ABC", 1, ops.get("ABC", 1)) // works
ops.set("ABC", 1, 'X') // type mismatch; found : Char('X') required: ops.Elem
Because types are path dependent the compiler complains when trying to use this, the error is:
type mismatch;
found : Char('X')
required: ops.Elem
You and I know ops.Elem
is Char
. My current workaround is to use Elem
as a type parameter instead:
trait Container[T, Elem] {
def get(c: T, i: Int): Elem
def set(c: String, i: Int, v: Elem): T
}
implicit object StringContainer extends Container[String, Char] {
def get(c: String, i: Int) = c(i)
def set(c: String, i: Int, v: Char) = c.patch(i, Seq(v), 1)
}
The drawback is to recall the type class when needed one needs to provide all type arguments:
val ops = implicitly[Container[String, Char]]
Is there some way to define types in type-class so that they can be used as path-independent?
You are requesting just
Container[String]
instead of
Container[String] { type Elem = Char }
Try with type refinement
object Container {
implicit val strContainer: Container[String] { type Elem = Char } = new Container[String] {
type Elem = Char
def get(c: String, i: Int) = c(i)
def set(c: String, i: Int, v: Char) = c.patch(i, Seq(v), 1)
}
}
val ops = implicitly[Container[String] { type Elem = Char }]
ops.set("ABC", 1, 'X') // ok
which with Aux pattern becomes something like
object Container {
type Aux[T,Elem0] = Container[T] { type Elem = Elem0 }
implicit val strContainer: Aux[String, Char] = new Container[String] {
type Elem = Char
def get(c: String, i: Int) = c(i)
def set(c: String, i: Int, v: Char) = c.patch(i, Seq(v), 1)
}
}
val ops = implicitly[Container.Aux[String,Char]]
ops.set("ABC", 1, 'X') // ok