8 Stimmen

Lesermonade mit Scalaz

Ich versuche, die Reader-Monade mit scalaz wie folgt zu definieren:

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)
}

aber ich erhalte einen Compilerfehler:

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

Warum ist das so?

Danke! Levi

16voto

retronym Punkte 54220

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]
    }
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X