Search code examples
scalafunctorscala-catscomonad

Cannot find a functor instance for Tuple2K


I have a toy DSL

case class Logging[A](msg: String, action: A)
case class Persist[A](msg: String, action: A)
type Effect[A] = EitherK[Logging, Persist, A]

that I want to pair with an equally toy interpreter

case class CoLogging[A](run: String => A)
case class CoPersist[A](run: String => A)
type Interp[A] = Tuple2K[CoLogging, CoPersist, A]

Here is an program example:

def prog(implicit L: Logs[Effect], P: Persists[Effect]): Free[Effect, Unit] =
  P.store("bar") >> L.log("foo")

and here is the interpreter:

def interpretEffect(implicit CL: CoLogs[IO], CP: CoPersists[IO]): Cofree[Interp, IO[Unit]] = 
  Cofree.unfold(IO.pure(())) { a: IO[Unit] => Tuple2K(CoLogging(CL.coLog(a)), CoPersist(CP.coPersist(a))) }

I've paid due diligence and defined functors as well as injection implicits. The compiler complains that it cannot find an instance cats.Functor[[A]cats.data.Tuple2K[example.CoLogging,example.CoPersist,A]], even though I am importing cats.data.Tuple2K._ where the instance is implicitly defined.

I can't see what I'm doing wrong, it must be something stupid. Do you have any idea? All the code can be seen in this gist.


Solution

  • The compiler complains that it cannot find an instance cats.Functor[[A]cats.data.Tuple2K[example.CoLogging,example.CoPersist,A]], even though I am importing cats.data.Tuple2K._ where the instance is implicitly defined.

    Functor[Tuple2K[F, G, ?]] is defined via Tuple2KInstances8#catsDataFunctorForTuple2K if Functor[F] and Functor[G] were defined. The thing is that Functor[CoLogging] and Functor[CoPersist] weren't.

    Add

    object CoLogging {
      implicit val coLoggingFunctor: Functor[CoLogging] = new Functor[CoLogging] {
        override def map[A, B](fa: CoLogging[A])(f: A => B): CoLogging[B] = CoLogging(fa.run andThen f)
      }
    }
    

    and

    object CoPersist {
      implicit val coPersistFunctor: Functor[CoPersist] = new Functor[CoPersist] {
        override def map[A, B](fa: CoPersist[A])(f: A => B): CoPersist[B] = CoPersist(fa.run andThen f)
      }
    }
    

    and everything should compile.

    The thing is the order of implicits. Move object functors to the beginning and everything should compile.