读者Monad与Scalaz

Lev*_*pan 8 monads scala scalaz

我尝试用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)
}
Run Code Online (Sandbox Code Playgroud)

但是我收到编译错误:

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

这是为什么?

谢谢,Levi

ret*_*nym 16

即使按照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
           ^
Run Code Online (Sandbox Code Playgroud)

如果您对程序的语法解释感到困惑,那么最好跑去scalac -Xprint:parser查看正在发生的事情.同样,您可以使用-Xprint:typer-Xprint:jvm查看程序转换的后续阶段.

那么,你怎么打电话>>=给你Reader?首先,你需要明确地传递类型参数Envenv.然后Reader[Env, Env]必须将结果转换为a MA[M[_], A].对于简单类型构造函数,隐式转换MAs#ma就足够了.但是,Reader必须部分应用两个param类型构造函数- 这意味着无法推断它,而是必须提供特定的隐式转换.

如果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]
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我猜你是来自哈斯克尔.Scala不能与Hindley-Milner推断竞争,不强制执行纯度,并且默认是严格的.它确实有JVM互操作,隐式params可以编码一些类型类很棘手的东西. (4认同)
  • 谢谢.这就是诀窍.我不得不承认,当我尝试将Scala实际用作功能性语言时,Scala确实令我失望,因为它对我来说感觉像是一个巨大的黑客. (3认同)