为类型类定义Monad,Applicative和Functor实例

时间:2019-02-06 21:56:06

标签: scala functional-programming scalaz

假设我已经为缓存计算定义了一个类型类。

trait Cached[F[_], A] {
  def value: F[A]
}

直观地讲,Cached包装了计算,因此我们可以在运行时对其进行评估,也可以从数据库中加载结果。

我想为此特征定义Functor,Applicative和Monad实例。使用Kind-projector使我的生活更轻松:

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))
    }
}

到目前为止,太好了。现在,让我们在一个简单的示例中使用monad:

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

我知道发散的隐式扩展是在编译器扩展隐式定义时陷入循环时发生的,但是我不明白为什么我的代码会出现这种情况。

如果有人能指出我正确的方向,我将不胜感激。我对函数式编程概念还很陌生,因此我在这里所做的甚至可能没有任何意义!

2 个答案:

答案 0 :(得分:2)

编译器不知道您的方法point是引用applicative还是引用monad

Monad类型类通常用于扩展Applicatives,因为每个monad实际上都是一个Applicative仿函数(加上“ join”,在Scala中称为“ flatten”)。如果要避免层次结构,并且希望Monad和Appadatives都定义自己的point,则需要使用不同的名称或以某种方式告诉编译器您要指的是哪一个(例如,通过类型参数)

答案 1 :(得分:0)

我最终定义了这样的实例:

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)
    }