Правильный синтаксис для обновления вложенной карты с помощью 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)

Я хочу обновить ключ "приложение" в labelsMap, Но я не могу получить следующее для компиляции:

(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"
)
Другие вопросы по тегам