I've got a trait which has an implicit cats.Applicative
, and I have a case class which implements that trait. I want to be able to call methods from cats.syntax.apply
on tuples of that case class, but the compiler doesn't realize it needs to go looking for the Applicative for the trait, and instead fails while trying to look for the Applicative for the case class.
For example: (note this is not my actual class, just a distillation of the problem)
import cats.arrow.FunctionK
import cats.Applicative
import cats.syntax.apply._
// The trait
trait GenericLookup[+A] {
def get(map: FunctionK[Key, Option]): Option[A]
}
object GenericLookup {
implicit val applicative: Applicative[GenericLookup] = /* ... */
}
// The subclass
case class Key[A](keyword: String) extends GenericLookup[A] {
def get(map: FunctionK[Key, Option]) = map(this)
}
// Some instances
val Foo = Key[String]("foo")
val Bar = Key[Boolean]("bar")
val Age = Key[Int]("age")
I want to be able to do e.g. (Foo, Bar, Age).tupled
to get a GenericLookup[(String, Boolean, Int)]
, or call (Foo, Bar, Age).mapN { /* ... */ }
to get a GenericLookup[Whatever]
. But:
val fba = (Foo, Bar, Age).tupled
// could not find implicit value for parameter invariant: cats.Invariant[Key]
It wants to find a typeclass for Key
, whereas I want it to find the typeclass for GenericLookup
.
The best thing I've come up with so far is to add
def generic: GenericLookup[A] = this
to the Key
class, which lets me do
(Foo.generic, Bar, Age).tupled
which seems to convince the compiler to go find the typeclass for GenericLookup instead of Key...
Is there a more automatic way I could set things up that would allow the use of (Foo, Bar, Age).tupled
without having to add extra operators or syntax?