Consider this example, where Listable
is intended to mixed into the companion object of a case class. Therefore, in order to call Writer.grid
, one must have a companion object A
that extends Listable[A]
, with an implicit Writer[A]
defined within. (Say for example, to convert a list of an arbitrary Listable
to a CSV format.)
trait Listable[A] {
def list: List[A]
}
object Writer {
def grid[A <: Listable[A]](listable: A)(implicit w: Writer[A]): String = {
listable.list.map(w.write(_).mkString(",")).mkString("\n")
}
}
trait Writer[A] {
def write(a: A): List[String]
}
And here's a naive implementation:
case class Test(id: Int, text: String)
object Test extends Listable[Test] {
def list = List(Test(1, "test"))
implicit val wrt = new Writer[Test] {
def write(t: Test) = List(t.id.toString, t.text)
}
}
This compiles, but cannot work because listable: A
really refers to the object Test
, and A
in w: Writer[A]
refers to the case class Test
, so calling Writer.grid(Test)
fails to conform to the type bounds.
I can work around this problem somewhat by ditching Listable
and requiring an implicit List[A]
in the signature of grid
:
def grid[A](implicit w: Writer[A], list: List[A]): String = ...
But I'd prefer to:
- Not require such an implicit function that could produce unexpected results.
- Not use a special type to wrap
list
, as it will also be used elsewhere. - Keep the definition of the
grid
method outside ofListable
.
Is it possible to re-work the signature of Writer.grid
to make this work? (or other structural changes)
Basically Engene pointed out a working solution, I just want to say that, what's really weird about your situation is that, you don't need the
listable
instance in the grid function. This fact is not that obvious from your original code, but it's pretty on the table if you see from Eugene's code, the argument is not used at all.So I'm guessing what you're trying to do is to have the companion object to have an instance of special instances for special purposes, maybe I'm wrong. But the name
Listable[A]
is really confusing, if you're trying to describe some trait of the companion object, you can name sth likeListableMeta[A]
, which makes more sense.What you need to do is basically
Writer.grid[Test]
instead of passing a concrete instance, so this should work. But the clear point here is to use typeclasses on all things, mix and match typeclasses and type bounds can cause confusion.