Dieser Fehler ist ziemlich undurchsichtig, selbst für Scala-Verhältnisse. Methodennamen, die mit =
werden besonders behandelt - sie werden zunächst als normaler Bezeichner betrachtet, und wenn dies nicht der Fall ist, werden sie zu einer Selbstzuweisung erweitert.
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
^
Wenn Sie über die syntaktische Interpretation Ihres Programms verwirrt sind, ist es eine gute Idee, den Befehl scalac -Xprint:parser
um zu sehen, was vor sich geht. In ähnlicher Weise können Sie mit -Xprint:typer
o -Xprint:jvm
um spätere Phasen der Umgestaltung des Programms zu sehen.
Wie rufen Sie also >>=
zu Ihrem Reader
? Zunächst müssen Sie das Argument type explizit übergeben Env
a env
. Das Ergebnis Reader[Env, Env]
muss dann in eine MA[M[_], A]
. Bei einfachen Typkonstruktoren ist die implizite Konvertierung MAs#ma
ausreichen wird. Allerdings ist der Konstruktor mit zwei Parametern Reader
muss teilweise angewandt werden - das bedeutet, dass es nicht abgeleitet werden kann und Sie stattdessen eine spezifische implizite Umwandlung vorsehen müssen.
Die Situation würde sich erheblich verbessern, wenn Adriaan jemals einen freien Nachmittag findet, um Implementierung einer Vereinheitlichung höherer Ordnung für die Inferenz von Typkonstruktoren . :)
Bis dahin, hier ist Ihr Code. Ein paar weitere Kommentare sind 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]
}
}