首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >为类型类定义Monad、Applicative和Functor实例

为类型类定义Monad、Applicative和Functor实例
EN

Stack Overflow用户
提问于 2019-02-07 05:56:06
回答 2查看 84关注 0票数 1

假设我定义了一个用于缓存计算的类型类。

代码语言:javascript
运行
复制
trait Cached[F[_], A] {
  def value: F[A]
}

直观地说,Cached封装了计算,因此我们可以在运行时评估它,也可以从数据库加载结果。

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

代码语言:javascript
运行
复制
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:

代码语言:javascript
运行
复制
import Cached._
val y = Cached(2.point[Id])
val z = for {
  a <- Cached(1.point[Id])
  b <- y
} yield a + b

运行代码时,我在运行时得到以下错误:

代码语言:javascript
运行
复制
[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

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

如果有人能告诉我正确的方向,我将不胜感激。我是函数式编程概念的新手,所以我在这里所做的事情可能没有任何意义!

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-02-08 01:49:37

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

代码语言:javascript
运行
复制
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)
    }
票数 0
EN

Stack Overflow用户

发布于 2019-02-07 06:03:21

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

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

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54563154

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档