FAQs

Does mainecoon support algebras with extra type parameters?

Yes. e.g.

import mainecoon._
import cats.~>
import util.Try

@autoFunctorK @finalAlg
trait Foo[F[_], T] {
  def a(i: Int): F[T]
}

implicit val tryFoo: Foo[Try, String] = new Foo[Try, String] {
  def a(i: Int) = Try(i.toString)
}

implicit val fk: Try ~> Option = λ[Try ~> Option](_.toOption)

import Foo.autoDerive._
Foo[Option, String].a(3)
// res0: Option[String] = Some(3)

Does mainecoon support algebras with abstract type member?

Yes but with some caveats. The FunctorK instance it generates does not refine to the type member. E.g.

@autoFunctorK @finalAlg
trait Bar[F[_]] {
  type T
  def a(i: Int): F[T]
}

implicit val tryInt = new Bar[Try] {
   type T = String
   def a(i: Int): Try[String] = Try(i.toString)
}

import Bar.autoDerive._

If you try to map this tryInt to a Bar[Option], the type T of the Bar[Option] isn’t refined. That is, you can do

Bar[Option].a(3)
// res1: Option[Bar[Option]#T] = Some(3)

But you can’t create a Bar[Option]{ type T = String } from the tryInt using FunctorK.

scala> val barOption: Bar[Option] { type T = String } = tryInt.mapK(fk)
<console>:31: error: value mapK is not a member of Bar[scala.util.Try]{type T = String}
       val barOption: Bar[Option] { type T = String } = tryInt.mapK(fk)
                                                               ^

However, there is also mapK function added to the companion object of the algebra which gives you more precise type.

val barOption: Bar[Option] { type T = String } = Bar.mapK(tryInt)(fk)
// barOption: Bar[Option]{type T = String} = Bar$$anon$3@1b1d398c

Also since the FunctorK (or InvariantK) instance uses a dependent type on the original interpreter, you may run into dependent type related issues. In those cases, this mapK (or imapK) on the companion object may give better result. Here are two examples.

Cannot resolve implicit defined by the dependent type

import cats.Show
import cats.implicits._

@autoFunctorK
trait Algebra[F[_]] {
  type TC[T]
  def a[T: TC](t: T): F[String]
}

object tryInt extends Algebra[Try] {
  type TC[T] = Show[T]
  def a[T: TC](t: T): Try[String] = Try(t.show)
}

FunctorK.mapK will result in unusable interpreter due to scalac’s difficulty in resolving implicit based on dependent type.

scala> FunctorK[Algebra].mapK(tryInt)(fk).a(List(1,2,3))
<console>:36: error: could not find implicit value for evidence parameter of type _1.TC[List[Int]]
       FunctorK[Algebra].mapK(tryInt)(fk).a(List(1,2,3))
                                           ^

The mapK on the companion will work fine.

Algebra.mapK(tryInt)(fk).a(List(1,2,3))
// res3: Option[String] = Some(List(1, 2, 3))

Cannot take in argument whose type is a dependent type

@autoInvariantK
trait InvAlg[F[_]] {
  type T
  def a(i: F[T]): F[T]
}

object tryInt extends InvAlg[Try] {
  type T = String
  def a(i: Try[String]): Try[String] = i.map(_ + "a")
}
implicit val rk: Option ~> Try = λ[Option ~> Try](o => Try(o.get))

InvariantK.imapK will result in unusable interpreter because method a’s argument type is a dependent type on original interpreter.

scala> InvariantK[InvAlg].imapK(tryInt)(fk)(rk).a(Some("4"))
<console>:37: error: type mismatch;
 found   : String("4")
 required: _1.T where val _1: InvAlg[Option]
       InvariantK[InvAlg].imapK(tryInt)(fk)(rk).a(Some("4"))
                                                       ^

The imapK on the companion will work fine

InvAlg.imapK(tryInt)(fk)(rk).a(Some("4"))
// res5: Option[String] = Some(4a)

I am seeing diverging implicit expansion for type MyAlgebra[F]

If you see error likes the following when you try to summon a specific instance of MyAlgebra

diverging implicit expansion for type MyAlgebra[F]

[error] starting with method autoDeriveFromFunctorK in object MyAlgebra

It probably means that necessary implicit MyAlgebra instance and/or the corresponding FunctionK is missing in scope.