在使用复合光学器件构建的路径中定义可选值

Defining optional values in a path built with composed optics

我有一个用默认值创建的嵌套大小写结构 类:

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

我想使用默认值创建整个事物,然后使用 Monocle 修改特别需要非默认值的元素。

使用 isos 很容易。我可以用composition指定navigation,然后用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"))

不幸的是,对于 prims,它似乎并不那么优雅,因为 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

您可以使用 Prism 中的 below 例如光学成分中的类型匹配:

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))))