Correct syntax for updating nested map using Monocle

独自空忆成欢 提交于 2019-12-11 07:14:23

问题


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 type B (or A itself if it's missing);
  • build a new object of type A by replacing its component of type B (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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!