scala Entweder.RechteProjektion Verwirrung (zum Verständnis Entzuckerung)
Frage
Ich kann ein verwenden =
in einer Scala für das Verständnis (wie im Abschnitt angegeben). 6.19 der SLS) wie folgt:
Möglichkeit
Angenommen, ich habe eine Funktion String => Option[Int]
:
scala> def intOpt(s: String) = try { Some(s.toInt) } catch { case _ => None }
intOpt: (s: String)Option[Int]
Dann kann ich es so nutzen
scala> for {
| str <- Option("1")
| i <- intOpt(str)
| val j = i + 10 //Note use of = in generator
| }
| yield j
res18: Option[Int] = Some(11)
Nach meinem Verständnis war dies im Wesentlichen gleichbedeutend mit:
scala> Option("1") flatMap { str => intOpt(str) } map { i => i + 10 } map { j => j }
res19: Option[Int] = Some(11)
Das heißt, der eingebettete Generator war eine Möglichkeit, a zu injizieren map
in eine Folge von flatMap
Anrufe.So weit, ist es gut.
Entweder.RightProjection
Was ich eigentlich tun möchte: Verwenden Sie zum Verständnis ein ähnliches Verständnis wie im vorherigen Beispiel Either
Monade.
Wenn wir es jedoch in einer ähnlichen Kette verwenden, diesmal jedoch mit Either.RightProjection
Monade/Funktor, es funktioniert nicht:
scala> def intEither(s: String): Either[Throwable, Int] =
| try { Right(s.toInt) } catch { case x => Left(x) }
intEither: (s: String)Either[Throwable,Int]
Dann benutze:
scala> for {
| str <- Option("1").toRight(new Throwable()).right
| i <- intEither(str).right //note the "right" projection is used
| val j = i + 10
| }
| yield j
<console>:17: error: value map is not a member of Product with Serializable with Either[java.lang.Throwable,(Int, Int)]
i <- intEither(str).right
^
Das Problem hat etwas mit der Funktion zu tun, die eine Rechtsprojektion als Argument für sie erwartet flatMap
Methode (d. h.es erwartet eine R => Either[L, R]
).Aber ändern, um nicht anzurufen right
Auf dem zweiten Generator lässt es sich immer noch nicht kompilieren.
scala> for {
| str <- Option("1").toRight(new Throwable()).right
| i <- intEither(str) // no "right" projection
| val j = i + 10
| }
| yield j
<console>:17: error: value map is not a member of Either[Throwable,Int]
i <- intEither(str)
^
Mega-Verwirrung
Aber jetzt bin ich doppelt verwirrt.Folgendes funktioniert einwandfrei:
scala> for {
| x <- Right[Throwable, String]("1").right
| y <- Right[Throwable, String](x).right //note the "right" here
| } yield y.toInt
res39: Either[Throwable,Int] = Right(1)
Dies bedeutet jedoch nicht:
scala> Right[Throwable, String]("1").right flatMap { x => Right[Throwable, String](x).right } map { y => y.toInt }
<console>:14: error: type mismatch;
found : Either.RightProjection[Throwable,String]
required: Either[?,?]
Right[Throwable, String]("1").right flatMap { x => Right[Throwable, String](x).right } map { y => y.toInt }
^
Ich dachte, das wäre gleichwertig
- Was ist los?
- Wie kann ich eine einbetten?
=
Generator in einem zum Verständnis über einEither
?
Lösung
Die Tatsache, dass Sie das nicht einbetten können =
im Für-Verstehen steht im Zusammenhang mit dieses Problem berichtet von Jason Zaugg;Die Lösung liegt in der Rechtsorientierung Either
(oder erstellen Sie einen neuen Datentyp, der dazu isomorph ist).
Zu Ihrer Mega-Verwirrung: Sie haben das Wort für Zucker falsch erweitert.Die Entzuckerung von
for {
b <- x(a)
c <- y(b)
} yield z(c)
Ist
x(a) flatMap { b =>
y(b) map { c =>
z(c) }}
und nicht
x(a) flatMap { b => y(b)} map { c => z(c) }
Daher hätten Sie Folgendes tun sollen:
scala> Right[Throwable, String]("1").right flatMap { x => Right[Throwable, String](x).right map { y => y.toInt } }
res49: Either[Throwable,Int] = Right(1)
Mehr Spaß beim Entzuckern (das Problem „j = i + 10“)
for {
b <- x(a)
c <- y(b)
x1 = f1(b)
x2 = f2(b, x1)
...
xn = fn(.....)
d <- z(c, xn)
} yield w(d)
wird entzuckert
x(a) flatMap { b =>
y(b) map { c =>
x1 = ..
...
xn = ..
(c, x1, .., xn)
} flatMap { (_c1, _x1, .., _xn) =>
z(_c1, _xn) map w }}
Also in deinem Fall, y(b)
hat einen Ergebnistyp Either
was nicht der Fall ist map
definiert.