【问题标题】:Defining Monad, Applicative, and Functor instances for a type class为类型类定义 Monad、Applicative 和 Functor 实例
【发布时间】:2019-06-30 23:14:53
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: scala functional-programming scalaz


    【解决方案1】:

    编译器不知道您的方法point 是指applicative 之一还是monad 之一。

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

    【讨论】:

    • 感谢您的回答。我没有定义层次结构的问题。我的印象是,传递给 monad 实例的隐式参数 appfunc 将确保 monad 中的 point 与 applicative 中的 point 相同。我在定义这些实例时遵循了typelevel.org/cats/typeclasses/monad.html 此处的示例。
    • 您可以同时使用 applicative 和 monad 作为有效的隐式转换。 appfunc 促成了 monad 转换是有效的这一事实(如果其中一个缺失,则不会应用转换)。所以 applicative 和 monad 都试图参与进来。猫在他们的文档中没有这个问题的原因是因为他们只定义了 monad 隐式转换(它搭载了现有的 applicative 类型类)。他们的代码中没有适用的隐式转换。
    【解决方案2】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-05
      • 2012-01-29
      • 1970-01-01
      • 2015-07-02
      • 2012-11-12
      • 1970-01-01
      相关资源
      最近更新 更多