Определение дополнительных значений в пути, построенном с помощью составной оптики

У меня есть структура вложенных классов case, которые я создаю со значениями по умолчанию:

case class Alpha(text: String = "content")
case class Beta(alpha: Alpha = Alpha())
case class Gamma(beta: Option[Beta] = None)

Я хотел бы создать все это со значениями по умолчанию, а затем изменить элементы, в частности, которые должны быть нестандартными, используя Monocle.

С isos это просто. Я могу указать навигацию с помощью композиции, а затем использовать set для изменения внутреннего элемента:

object Beta {
  val alphaI: Iso[Beta, Alpha] = GenIso[Beta, Alpha]
}
object Alpha {
  val textI: Iso[Alpha, String] = GenIso[Alpha, String]
}

(Beta.alphaI composeIso Alpha.textI).set("foo")(Beta()) shouldBe Beta(Alpha("foo"))

К сожалению, с примесями это не кажется таким элегантным, как set/modify изменит только внутренний элемент, если все параметры во время навигации определены (Some(...))

object Gamma {
  val betaI: Iso[Gamma, Option[Beta]] = GenIso[Gamma, Option[Beta]]
  val betaP: Prism[Gamma, Beta] = Prism[Gamma, Beta](_.beta)(a => Gamma(Some(a)))
}

val navigateToText: Prism[Gamma, String] = Gamma.betaP composeIso Beta.alphaI composeIso Alpha.textI

navigateToText.set("foo")(Gamma(None)) shouldBe Gamma(None)
navigateToText.set("foo")(Gamma(Some(Beta()))) shouldBe Gamma(Some(Beta(Alpha("foo"))))

Лучшее, что я придумал, это использование составной оптики для Some() каждый необязательный элемент пути, а затем составная оптика, чтобы установить элемент, который нас интересовал для начала.

(Gamma.betaI.set(Some(Beta())) andThen navigateToText.set("foo")) (Gamma()) shouldBe Gamma(Some(Beta(Alpha("foo"))))

В идеале я бы хотел, чтобы каждый строительный блок композиции устанавливал необязательные значения на Some(defaultValue) если они изначально None, Очевидно, что строительный блок должен быть определен, включая соответствующее значение по умолчанию для шага пути. Какие-либо предложения?

Полный код, включая импорт: https://github.com/jcaraballo/navigating-fixtures-with-monocle/blob/master/src/test/scala/pr/NavigateIntoOptionSpec.scala

1 ответ

Решение

Вы могли бы использовать below от Prism такие как типы совпадают в составе оптики:

import monocle.macros.GenIso
import scalaz.std.option._
case class Alpha(text: String = "content")
case class Beta(alpha: Alpha = Alpha())
case class Gamma(beta: Option[Beta] = None)

val navigateToText = GenIso[Gamma, Option[Beta]] composePrism
  GenIso[Beta, Alpha].asPrism.below[Option] composePrism
  GenIso[Alpha, String].asPrism.below[Option]

navigateToText.set(Some("foo"))(Gamma(None))                     // Gamma(Some(Beta(Alpha(foo))))
navigateToText.set(Some("foo"))(Gamma(Some(Beta(Alpha("bar"))))) // Gamma(Some(Beta(Alpha(foo))))
Другие вопросы по тегам