Правильный синтаксис для обновления вложенной карты с помощью Monocle
Я видел официальный пример обновления Map
но у меня проблемы с синтаксисом.
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)
Я хочу обновить ключ "приложение" в labels
Map
, Но я не могу получить следующее для компиляции:
(labels.composeOptional(index("app"))).set("whatever")(someLabels)
На самом деле, этот ответ одного из авторов Monacle не компилируется.
2 ответа
Не имея определения вашего класса Event, у меня нет точного ответа, но, следуя руководству и примеру университета, я могу обновить вложенную Карту последней версией на момент написания, monocle 1.5.0-cats-M1, Убедитесь, что в вашем проекте есть как банки с ядром monocle-core, так и файлы monocle-macro. Затем,
import monocle.macros.GenLens
import monocle.function.At.at // // to get at Lens
import monocle.std.map._ // to get Map instance for At
Затем, следуя примеру университета,
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)
))))
я смогу
(departments composeLens at("History")).set(Some(Department(30, List(Lecturer("arnold", "stones", 30)))))(uni)
Основными отличиями от вашего кода выше являются использование at() и перенос Департамента с Some для соответствия типу возвращаемого значения Option при доступе с использованием ключа для получения значения из Map.
Учитывая, что someLabels
имеет тип Map[String, String]
, ваш код либо избыточен, либо содержит неверный аргумент Optional
, Если мы упростим подпись composeOptional
метод в Lens[S, A]
, это дает:
def composeOptional(other: Optional[A, B]): Optional[S, B]
Optional[A, B]
в очень неточном приближении соответствует косвенности, которая позволяет:
- смотреть на значение типа
A
и получить его компонент типаB
(или жеA
сам, если его не хватает); - построить новый объект типа
A
заменив его компонент типаB
(или просто вернуть оригинальный объект, если такого компонента нет).
labels composeOptional index("app")
доходность Optional[Metadata, String]
, Это, очевидно, не будет работать на Map[String, String]
: это от Metadata
в Map[String, String]
(с помощью labels
) а затем сразу из Map[String, String]
к его String
элемент (через index("app")
), скрывая доступ к карте от пользователя целиком. Если вы пытаетесь просто установить значение для данного ключа в someLabels
карта, достаточно использовать 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")
Ваш состав Optional
с другой стороны, работает над 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")
Я проверил это с помощью следующих версий (в 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"
)