Контекстная граница скаляра.Coyoneda.liftTF

Закончив смотреть заставляющий задуматься видеоролик "Архитектура составного приложения с недорогими монадами" от Рунара Бьярнасона, я начал писать примеры, представленные в видео, в Scalaz. У меня небольшой сюрприз при реализации интерпретатора на основе писателя для приложения Interact. Вот код,

sealed trait Interact[A]

case class Ask(prompt: String) extends Interact[String]

case class Tell(msg: String) extends Interact[Unit]

type W[A] = scalaz.Writer[Vector[String], A]

object ConsoleW extends (Interact ~> W) {
  def apply[A](i: Interact[A]) = i match {
    case Ask(prompt) =>
      println(prompt)
      Writer(Vector(prompt), readLine)
    case Tell(msg) =>
      println(msg)
      Writer(Vector.empty, ())
  }
}

Когда я попытался поднять ConsoleW в интерпретатор для интерактивной монады, компилятор намекнул на отсутствие привязки контекста Functor к W. Я был очень удивлен, так как Writer сам является монадой, границы контекста функтора должны быть предоставлены бесплатно. Итак, я должен был явно написать функтор,

implicit val functor = new Functor[W] {
  def map[A, B](fa: Writer[Vector[String], A])(f: A => B) =
    fa.map(f)
}

что довольно глупо... поскольку я в основном не делал ничего, кроме вызова метода map самого Writer. Теперь я могу поднять ConsoleW в переводчик. Однако, когда я попытался свернуть карту взаимодействующей программы с ConsoleW, компилятор снова намекнул на отсутствие контекста Monad, привязанного к W! Хорошо, это совершенно неожиданно, позволь мне покормить тебя ложкой, Скалак...

val prg: Free[({type f[x] = Coyoneda[Interact, x]})#f, Unit] = ???

import scalaz.std.vector._

implicit val monad = new scalaz.Monad[W] {
  def point[A](a: => A) = Writer[Vector[String], A](Vector.empty, a)
  def bind[A, B](fa: Writer[Vector[String], A])(f: A => Writer[Vector[String], B]) = 
    fa.flatMap(f)
}

prg.foldMap(Coyoneda.liftTF(ConsoleW))

Теперь все компилируется, и Writer Monad может записывать все запросы в вектор при запуске программы. Но это неутешительно, я хотел бы создать интерпретатор с Writer в качестве основной монады без необходимости явного предоставления доказательства того, что это действительно Functor и Monad. Есть идеи, как это может сработать?

1 ответ

Решение

Просто нужно импортировать scalaz.std.vector._, и проблема решена. Я был впечатлен тем, как Id.Id мог просто работать как есть, и забыл, что импорт монады писателя должен быть более сложным.

Другие вопросы по тегам