Let's say I have defined a type class for Caching computations.
trait Cached[F[_], A] {
def value: F[A]
}
Intuitively, Cached
wraps the computation so we can either evaluate it at run time or load the result from the database instead.
I would like to define Functor, Applicative, and Monad instances for this trait. Using Kind-projector to make my life easier:
import scalaz._, Scalaz._
object Cached {
def apply[F[_], A](f: => F[A]): Cached[F, A] = new Cached[F, A] {
override def value: F[A] = f
}
implicit def functor[F[_] : Functor]: Functor[Cached[F, ?]] = new Functor[Cached[F, ?]] {
override def map[A, B](fa: Cached[F, A])(f: A => B): Cached[F, B] =
Cached(fa.value map f)
}
implicit def applicative[F[_] : Applicative]: Applicative[Cached[F, ?]] = new Applicative[Cached[F, ?]] {
override def point[A](a: => A): Cached[F, A] = Cached(a.point[F])
override def ap[A, B](fa: => Cached[F, A])(f: => Cached[F, A => B]): Cached[F, B] =
Cached(fa.value <*> f.value)
}
implicit def monad[F[_] : Monad](implicit app: Applicative[Cached[F, ?]], func: Functor[Cached[F, ?]]): Monad[Cached[F, ?]] =
new Monad[Cached[F, ?]] {
override def point[A](a: => A): Cached[F, A] = app.point(a)
override def bind[A, B](fa: Cached[F, A])(f: A => Cached[F, B]): Cached[F, B] =
Cached(func.map(fa)(f).value >>= (_.value))
}
}
So far, so good. Now, let's use the monad in a simple example:
import Cached._
val y = Cached(2.point[Id])
val z = for {
a <- Cached(1.point[Id])
b <- y
} yield a + b
Running the code, I get the following error at run time:
[error] diverging implicit expansion for type scalaz.Applicative[[β$4$]Cached[scalaz.Scalaz.Id,β$4$]]
[error] starting with method monad in object Cached
[error] a <- Cached(1.point[Id])
[error] ^
[error] diverging implicit expansion for type scalaz.Applicative[[β$4$]Cached[scalaz.Scalaz.Id,β$4$]]
[error] starting with method monad in object Cached
[error] b <- y
[error] ^
[error] two errors found
[error] (Test / compileIncremental) Compilation failed
I know diverging implicit expansion happens when the compiler stuck in a loop when expanding the implicit definitions, but I can't see why that is the case with my code.
I would appreciate if someone could point me to the right direction. I am pretty new to functional programming concepts so what I have done here might not even make sense!
I ended up defining the instances like this:
implicit def instance[F[_] : Monad]: Functor[Cached[F, ?]] with Applicative[Cached[F, ?]] with Monad[Cached[F, ?]] =
new Functor[Cached[F, ?]] with Applicative[Cached[F, ?]] with Monad[Cached[F, ?]] {
def eval[A](fa: => Cached[F, A]): F[A] = {
println("loading stuff from the database...")
fa.value
}
override def point[A](a: => A): Cached[F, A] =
Cached(a.point[F])
override def map[A, B](fa: Cached[F, A])(f: A => B): Cached[F, B] = {
Cached(eval(fa) map f)
}
override def bind[A, B](fa: Cached[F, A])(f: A => Cached[F, B]): Cached[F, B] = {
Cached(eval(fa) >>= (a => f(a).value))
}
override def ap[A, B](fa: => Cached[F, A])(f: => Cached[F, A => B]): Cached[F, B] =
Cached(eval(fa) <*> f.value)
}