Определение экземпляров Monad, Applicative и Functor для класса типов - PullRequest
0 голосов
/ 07 февраля 2019

Допустим, я определил класс типов для вычислений кэширования.

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

Интуитивно, Cached оборачивает вычисление, чтобы мы могли либо оценить его во время выполнения, либо вместо этого загрузить результат из базы данных.

Я хотел бы определить экземпляры Functor, Applicative и Monad для этой черты.Использование Kind-проектора для облегчения моей жизни:

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

Я знаю, что неявное расширение происходит, когдакомпилятор застрял в цикле при расширении неявных определений, но я не понимаю, почему это происходит с моим кодом.

Я был бы признателен, если бы кто-то мог указать мне правильное направление.Я довольно плохо знаком с концепциями функционального программирования, поэтому то, что я здесь сделал, может даже не иметь смысла!

Ответы [ 2 ]

0 голосов
/ 07 февраля 2019

В итоге я определил такие экземпляры как:

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 голосов
/ 07 февраля 2019

Компилятор не знает, относится ли ваш метод point к applicative единице или monad единице.

Классы типов монад часто создаются для расширения Аппликативов, поскольку каждая монада действительно является аппликативным функтором (плюс «соединение», известное в Scala как «сплющить»).Если вы хотите избежать иерархий и хотите, чтобы и ваши монады, и аппликативы определяли свои собственные point, то вам нужно либо назвать их по-другому, либо как-то указать компилятору, на какой вы ссылаетесь (например, через параметр типа).

...