读者 Monad 与 Scalaz

发布于 2024-09-10 12:01:55 字数 910 浏览 9 评论 0原文

我尝试使用 scalaz 定义 Reader monad,如下所示:

import scalaz._
import Scalaz._

final class Reader[E,A](private[Reader] val runReader: E => A)

object Reader {
  def apply[E,A](f: E => A) = new Reader[E,A](f)
  def env[E]: Reader[E,E] = Reader(identity _)

  implicit def ReaderMonad[E] = new Monad[PartialApply1Of2[Reader,E]#Apply] {
    def pure[A](a: => A) = Reader(_ => a)

    def bind[A,B](m: Reader[E,A], k: A => Reader[E,B]) =
      Reader(e => k(m.runReader(e)).runReader(e))
  }
}


object Test {
  import Reader._

  class Env(val s: String)

  def post(s: String): Reader[Env, Option[String]] =
    env >>= (e => if (e.s == s) some(s).pure else none.pure)
}

但出现编译器错误:

reader.scala:27: reassignment to val
     env >>= (e => if (e.s == s) some(s).pure else none.pure)
         ^

这是为什么?

谢谢, 列维

I try to define the Reader monad with scalaz like this:

import scalaz._
import Scalaz._

final class Reader[E,A](private[Reader] val runReader: E => A)

object Reader {
  def apply[E,A](f: E => A) = new Reader[E,A](f)
  def env[E]: Reader[E,E] = Reader(identity _)

  implicit def ReaderMonad[E] = new Monad[PartialApply1Of2[Reader,E]#Apply] {
    def pure[A](a: => A) = Reader(_ => a)

    def bind[A,B](m: Reader[E,A], k: A => Reader[E,B]) =
      Reader(e => k(m.runReader(e)).runReader(e))
  }
}


object Test {
  import Reader._

  class Env(val s: String)

  def post(s: String): Reader[Env, Option[String]] =
    env >>= (e => if (e.s == s) some(s).pure else none.pure)
}

but I get a compiler error:

reader.scala:27: reassignment to val
     env >>= (e => if (e.s == s) some(s).pure else none.pure)
         ^

Why is that?

Thanks,
Levi

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

寄离 2024-09-17 12:01:55

即使按照 Scala 的标准,这个错误也是相当不透明的。以 = 结尾的方法名称会被特殊对待——它们首先被视为普通标识符,如果失败,它们将扩展为自赋值。

scala> def env[A] = 0
env: [A]Int

scala> env >>= 0
<console>:7: error: reassignment to val
       env >>= 0
           ^

scala> env = env >> 0
<console>:6: error: reassignment to val
       env = env >> 0
           ^

如果您对程序的语法解释感到困惑,最好运行 scalac -Xprint:parser 来查看发生了什么。同样,您可以使用 -Xprint:typer-Xprint:jvm 查看程序转换的后续阶段。

那么,如何在 Reader 上调用 >>= 呢?首先,您需要将类型参数 Env 显式传递给 env。然后,必须将生成的 Reader[Env, Env] 转换为 MA[M[_], A]。对于简单类型构造函数,隐式转换 MAs#ma 就足够了。但是,两个参数类型构造函数 Reader 必须部分应用 - 这意味着它无法推断,而您必须提供特定的隐式转换。

如果 Adriaan 找到一个空闲的下午实现类型的高阶统一,情况将会大大改善构造函数推断。 :)

在那之前,这是您的代码。还有一些内联评论。

import scalaz._
import Scalaz._

final class Reader[E, A](private[Reader] val runReader: E => A)

object Reader {
  def apply[E, A](f: E => A) = new Reader[E, A](f)

  def env[E]: Reader[E, E] = Reader(identity _)

  implicit def ReaderMonad[E]: Monad[PartialApply1Of2[Reader, E]#Apply] = new Monad[PartialApply1Of2[Reader, E]#Apply] {
    def pure[A](a: => A) = Reader(_ => a)

    def bind[A, B](m: Reader[E, A], k: A => Reader[E, B]) =
      Reader(e => k(m.runReader(e)).runReader(e))
  }

  // No Higher Order Unification in Scala, so we need partially applied type constructors cannot be inferred.
  // That's the main reason for defining function in Scalaz on MA, we can create one implicit conversion
  // to extract the partially applied type constructor in the type parameter `M` of `MA[M[_], A]`.
  //
  // I'm in the habit of explicitly annotating the return types of implicit defs, it's not strictly necessary
  // but there are a few corner cases it pays to avoid.
  implicit def ReaderMA[E, A](r: Reader[E, A]): MA[PartialApply1Of2[Reader, E]#Apply, A] = ma[PartialApply1Of2[Reader, E]#Apply, A](r)
}


object Test {
  import Reader._

  class Env(val s: String)

  def post(s: String): Reader[Env, Option[String]] =
    // Need to pass the type arg `Env` explicitly here.
    env[Env] >>= {e =>
      // Intermediate value and type annotation not needed, just here for clarity.
      val o: Option[String] = (e.s === s).guard[Option](s)
      // Again, the partially applied type constructor can't be inferred, so we have to explicitly pass it.
      o.pure[PartialApply1Of2[Reader, Env]#Apply]
    }
}

This error is fairly opaque, even by Scala's standards. Method names ending with = are treated specially -- they are first considered as a normal identifier, and failing that, they are expanded to a self assignment.

scala> def env[A] = 0
env: [A]Int

scala> env >>= 0
<console>:7: error: reassignment to val
       env >>= 0
           ^

scala> env = env >> 0
<console>:6: error: reassignment to val
       env = env >> 0
           ^

If you're confused about the syntactic interpretation of your program, it's a good idea to run scalac -Xprint:parser to see what's going on. Similarly, you can use -Xprint:typer or -Xprint:jvm to see later phases of the program transformation.

So, how do you call >>= on your Reader? First of all, you'll need to explicitly pass the type argument Env to env. The resulting Reader[Env, Env] must then be converted to a MA[M[_], A]. For simple type constructors, the implicit conversion MAs#ma will suffice. However the two param type constructor Reader must be partially applied -- this means it can't be inferred and instead you must provide a specific implicit conversion.

The situation would be vastly improved if Adriaan ever finds a spare afternoon to implement higher-order unification for type constructor inference. :)

Until then, here's your code. A few more comments are inline.

import scalaz._
import Scalaz._

final class Reader[E, A](private[Reader] val runReader: E => A)

object Reader {
  def apply[E, A](f: E => A) = new Reader[E, A](f)

  def env[E]: Reader[E, E] = Reader(identity _)

  implicit def ReaderMonad[E]: Monad[PartialApply1Of2[Reader, E]#Apply] = new Monad[PartialApply1Of2[Reader, E]#Apply] {
    def pure[A](a: => A) = Reader(_ => a)

    def bind[A, B](m: Reader[E, A], k: A => Reader[E, B]) =
      Reader(e => k(m.runReader(e)).runReader(e))
  }

  // No Higher Order Unification in Scala, so we need partially applied type constructors cannot be inferred.
  // That's the main reason for defining function in Scalaz on MA, we can create one implicit conversion
  // to extract the partially applied type constructor in the type parameter `M` of `MA[M[_], A]`.
  //
  // I'm in the habit of explicitly annotating the return types of implicit defs, it's not strictly necessary
  // but there are a few corner cases it pays to avoid.
  implicit def ReaderMA[E, A](r: Reader[E, A]): MA[PartialApply1Of2[Reader, E]#Apply, A] = ma[PartialApply1Of2[Reader, E]#Apply, A](r)
}


object Test {
  import Reader._

  class Env(val s: String)

  def post(s: String): Reader[Env, Option[String]] =
    // Need to pass the type arg `Env` explicitly here.
    env[Env] >>= {e =>
      // Intermediate value and type annotation not needed, just here for clarity.
      val o: Option[String] = (e.s === s).guard[Option](s)
      // Again, the partially applied type constructor can't be inferred, so we have to explicitly pass it.
      o.pure[PartialApply1Of2[Reader, Env]#Apply]
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文