Определение дополнительных значений в пути, построенном с помощью составной оптики
У меня есть структура вложенных классов 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))))