问题
I've seen the official example of updating a Map
but I'm having trouble with the syntax.
val pod: Lens[Event, Pod] = GenLens[Event](_.`object`)
val metadata: Lens[Pod, Metadata] = GenLens[Pod](_.metadata)
val labels: Lens[Metadata, Map[String, String]] = GenLens[Metadata](_.labels)
I want to update a key "app" in the labels
Map
. But I can't get the following to compile:
(labels.composeOptional(index("app"))).set("whatever")(someLabels)
In fact, this answer by one of the authors of Monacle doesn't compile.
回答1:
Without having the definition of your Event class, I do not have an exact answer, but following the tutorial and the University example, I am able to update a nested Map with latest version as of this writing, monocle 1.5.0-cats-M1. Be sure to have both the monocle-core and the monocle-macros jars in your project. Then,
import monocle.macros.GenLens
import monocle.function.At.at // // to get at Lens
import monocle.std.map._ // to get Map instance for At
Then, following the university example,
case class Lecturer(firstName: String, lastName: String, salary: Int)
case class Department(budget: Int, lecturers: List[Lecturer])
case class University(name: String, departments: Map[String, Department])
val departments = GenLens[University](_.departments)
val uni = University("oxford", Map(
"Computer Science" -> Department(45, List(
Lecturer("john" , "doe", 10),
Lecturer("robert", "johnson", 16)
)),
"History" -> Department(30, List(
Lecturer("arnold", "stones", 20)
))))
I am able to
(departments composeLens at("History")).set(Some(Department(30, List(Lecturer("arnold", "stones", 30)))))(uni)
The major differences from your code above are the use of at() and wrapping of the Department with Some to correspond with an Option return type when accessing using a key to retrieve value from a Map.
回答2:
Considering that someLabels
is of type Map[String, String]
, your code is either excessive or just supplies wrong argument to composed Optional
. If we simplify signature of composeOptional
method in Lens[S, A]
, it yields:
def composeOptional(other: Optional[A, B]): Optional[S, B]
Optional[A, B]
, at the very imprecise approximation, corresponds to indirection that allows to:
- look into value of type
A
and get its component of typeB
(orA
itself if it's missing); - build a new object of type
A
by replacing its component of typeB
(or just return original object if there's no such component).
labels composeOptional index("app")
yields Optional[Metadata, String]
. That obviously won't work on Map[String, String]
: it indirects from Metadata
to Map[String, String]
(via labels
) and then immediately from Map[String, String]
to its String
element (via index("app")
), hiding map access from the user entirely. If you're trying to just set a value at a given key in someLabels
map, it suffices to use index
:
val someLabels1 = Map("app" -> "any")
val someLabels2 = Map("unit" -> "any")
index("app").set("whatever")(someLabels1) // Map("app" -> "whatever")
index("app").set("whatever")(someLabels2) // Map("unit" -> "any")
Your composed Optional
, on the other hand, works over Metadata
:
case class Metadata(labels: Map[String, String])
val someLabels = Map("app" -> "any")
val meta = Metadata(someLabels)
(labels composeOptional index("app")).set("whatever")(meta)
// Metadata(Map("app" -> "whatever")
I've checked it with following versions (in build.sbt
):
scalaVersion := 2.12.3
libraryDependencies ++= Seq(
"com.github.julien-truffaut" %% "monocle-core" % "1.4.0",
"com.github.julien-truffaut" %% "monocle-macro" % "1.4.0"
)
来源:https://stackoverflow.com/questions/47786656/correct-syntax-for-updating-nested-map-using-monocle