比方说,我已经定义了缓存计算的类型类。
trait Cached[F[_], A] {
def value: F[A]
}
直观地说,Cached
包装计算,所以我们可以在运行时对其进行评估或从数据库中加载的结果来代替。
我想定义函子,应用型,和单子实例此特征。使用类投影机,使我的生活更轻松:
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))
}
}
到现在为止还挺好。现在,让我们使用的单子在一个简单的例子:
import Cached._
val y = Cached(2.point[Id])
val z = for {
a <- Cached(1.point[Id])
b <- y
} yield a + b
运行代码,我会在运行时出现以下错误:
[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
我知道,当分歧扩大隐含的定义时,停留在循环,编译器隐式扩张发生,但是我不明白为什么这是我的代码的情况。
我将不胜感激,如果有人可以点我到正确的方向。我非常新的函数式编程概念,所以我在这里做甚至可能没有什么意义!
编译器不知道你的方法是否point
指的是applicative
一个或monad
之一。
单子类型类通常由延长Applicatives,因为每个单子确实是一个适用函子(加上“加盟”,Scala中被称为“扁平化”)。如果你想避免层次结构和希望双方你单子和applicatives定义自己point
,那么你需要或者不同的命名或以某种方式告诉你指的是(例如,通过类型参数)的一个编译器。
我结束了定义这样的实例:
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)
}