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
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.If you’re confused about the syntactic interpretation of your program, it’s a good idea to run
scalac -Xprint:parserto see what’s going on. Similarly, you can use-Xprint:typeror-Xprint:jvmto see later phases of the program transformation.So, how do you call
>>=on yourReader? First of all, you’ll need to explicitly pass the type argumentEnvtoenv. The resultingReader[Env, Env]must then be converted to aMA[M[_], A]. For simple type constructors, the implicit conversionMAs#mawill suffice. However the two param type constructorReadermust 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.