此错误是相当不透明的,甚至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在implement higher-order unification for type constructor inference找到一个空闲的下午,情况就会大大改善。 :)
在此之前,这是您的代码。还有几条评论是内联的。
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]
}
}
谢谢。这就是诀窍。我必须承认,虽然Scala真的令我失望,当我尝试使用它作为函数式语言时,因为它感觉像是一个巨大的黑客。 – 2010-07-19 21:12:42
我想你是从哈斯克尔来的。斯卡拉不能与Hindley-Milner推论竞争,不强制纯度,并且默认情况下是严格的。它确实有JVM互操作,隐式参数可以对类型类进行一些处理。 – retronym 2010-07-19 21:31:24