Monocle 是一个很棒的库(而不是唯一一个),它实现了镜头模式,如果我们必须在巨大的嵌套对象中更改一个字段,那就太好了。就像在例子http://julien-truffaut.github.io/Monocle/
case class Street(number: Int, name: String)
case class Address(city: String, street: Street)
case class Company(name: String, address: Address)
case class Employee(name: String, company: Company)
以下样板
employee.copy(
company = employee.company.copy(
address = employee.company.address.copy(
street = employee.company.address.street.copy(
name = employee.company.address.street.name.capitalize // luckily capitalize exists
)
)
)
)
可以很容易地替换为
import monocle.macros.syntax.lens._
employee
.lens(_.company.address.street.name)
.composeOptional(headOption)
.modify(_.toUpper)
这是伟大的。据我了解,宏魔术将所有内容完全转换为与上述相同的代码。
但是,如果我想组合几个动作怎么办?如果我想一键更改街道名称、地址城市和公司名称怎么办?如下所示:
employee.copy(
company = employee.company.copy(
address = employee.company.address.copy(
street = employee.company.address.street.copy(
name = employee.company.address.street.name.capitalize // luckily capitalize exists
),
city = employee.company.address.city.capitalize
),
name = employee.company.name.capitalize
)
)
如果我只是在这里重用镜头,我将有以下代码:
employee
.lens(_.company.address.street.name).composeOptional(headOption).modify(_.toUpper)
.lens(_.company.address.city).composeOptional(headOption).modify(_.toUpper)
.lens(_.company.name).composeOptional(headOption).modify(_.toUpper)
最终将被翻译成三个 employee.copy(...).copy(...).copy(...)
调用,而不仅仅是一个 employee.copy(...)
。如何让它变得更好?
此外,应用一系列操作真的很棒。就像成对序列一样Seq[(Lens[Employee, String], String => String)]
,第一个元素是指向正确视场的透镜,第二个是修改它的函数。这将有助于从外部构建这样的操作序列。对于上面的例子:
val operations = Seq(
GenLens[Employee](_.company.address.street.name) -> {s: String => s.capitalize},
GenLens[Employee](_.company.address.city) -> {s: String => s.capitalize},
GenLens[Employee](_.company.name) -> {s: String => s.capitalize}
)
或类似的东西...