这个问题没有提到修改数据,但是当你需要这样做时,你很快就会发现 Scala 库没有工具可以使这变得容易(当数据不可变时)。如果您还没有遇到过这种情况,请尝试编写一个函数,该函数将使用问题中定义的类型替换或修改 avalue
所持Cash
有的。Person
正如 Tony Morris 的Scala 中的非对称透镜中所述,透镜是解决此问题的合适方法。
这是一个示例,说明我们如何使用 Scalaz 的scalaz-7分支中的和(部分镜头)实现来访问和更新value
一个人的。Cash
Lens
PLens
首先,一些样板:为案例类的每个字段定义 Lens 实例。A @-@ B
意思相同Lens[A, B]
。
val pants: Person @-@ Option[Pants] =
lensG(_.pants, p => ps => p.copy(pants = ps))
val pocket: Pants @-@ Option[Pocket] =
lensG(_.pocket, ps => p => ps.copy(pocket = p))
val cash: Pocket @-@ Option[Cash] =
lensG(_.cash, p => c => p.copy(cash = c))
val value: Cash @-@ String =
lensG(_.value, c => v => c.copy(value = v))
但是,我们无法组合所有这些镜头,因为大多数字段都包含在Option
类型中。
救援的部分镜头:这些允许我们访问和更新结构中可能不存在的部分,例如 a 的Some
值Option
或head
a的值List
。
我们可以使用somePLens
Scalaz 7 中的函数来创建查看每个可选字段的部分镜头。然而,为了用我们的一个常规镜头组成一个部分镜头,我们需要使用partial
每个Lens
.
// @-? is an infix type alias for PLens
val someCash: Pocket @-? Cash = cash.partial andThen somePLens
scala> someCash.get(Pocket(Some(Cash("zilch"))))
res1: Option[Cash] = Some(Cash(zilch))
Person
以同样的方式,我们可以创建我们的部分镜头,通过组合我们所有镜头的partial
实例来查看 a 持有的现金,并将somePLens
. 在这里,我使用了<=<
运算符,一个别名 for andThen
(相当于compose
切换了操作数)。
val someCashValue: Person @-? String =
pants.partial <=< somePLens <=<
pocket.partial <=< somePLens <=<
cash.partial <=< somePLens <=<
value.partial
创建一个Person
实例来玩:
val ben = Person(Some(Pants(Some(Pocket(Some(Cash("zilch")))))))
使用部分镜头来访问我拥有的现金价值:
scala> someCashValue.get(ben)
res2: Option[String] = Some(zilch)
使用局部镜头修改数值:
scala> someCashValue.mod(_ + ", zero, nada", ben)
res3: Person = Person(Some(Pants(Some(Pocket(Some(Cash(zilch, zero, nada)))))))
现在,如果我没有穿任何裤子(!),我们可以看到修改我的现金价值的尝试是如何无效的:
scala> val ben = Person(None)
ben: Person = Person(None)
scala> someCashValue.mod(_ + ", zero, nada", ben)
res4: Person = Person(None)