pko*_*lov 7 scala monocle-scala
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)
Run Code Online (Sandbox Code Playgroud)
以下样板
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
)
)
)
)
Run Code Online (Sandbox Code Playgroud)
可以轻松更换
import monocle.macros.syntax.lens._
employee
.lens(_.company.address.street.name)
.composeOptional(headOption)
.modify(_.toUpper)
Run Code Online (Sandbox Code Playgroud)
哪个好.据我了解,宏魔术将所有内容完全转换为与上面相同的代码.
但是,如果我想结合几个动作怎么办?如果我想通过一次通话同时更改街道名称,地址城市和公司名称怎么办?如下:
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
)
)
Run Code Online (Sandbox Code Playgroud)
如果我只是在这里重复使用镜头,我会有以下代码:
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)
Run Code Online (Sandbox Code Playgroud)
哪个最终将转换为三个 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}
)
Run Code Online (Sandbox Code Playgroud)
或类似的东西......
据我了解,宏魔术将所有内容完全转换为与上面相同的代码.
它没有.
这个简单的代码:
employee.lens(_.name)
.modify(_.capitalize)
Run Code Online (Sandbox Code Playgroud)
变得像怪物那样的东西*:
monocle.syntax.ApplyLens(employee,
new monocle.PLens[Employee, Employee, String, String] {
def get(e: Employee): String = e.name;
def set(s: String): Employee => Employee = _.copy(s);
def modify(f: String => String): Employee => Employee = e => e.copy(f(e.name))
}
}).modify(_.capitalize)
Run Code Online (Sandbox Code Playgroud)
这与简单相去甚远
employee.copy(name = employee.name.capitalize)
Run Code Online (Sandbox Code Playgroud)
并包括三个冗余对象(匿名镜头类,ApplyLens用于语法糖和lambda返回modify).我们通过capitalize直接使用而不是用作曲来跳过更多headOption.
所以不,没有免费的晚餐.然而,大多数情况下,它足够好,没有人关心额外的镜头对象和中间结果.
你可以建立从多个镜头遍历(集光透镜),如果它们的类型一致(这里是Employee来String)
val capitalizeAllFields = Traversal.applyN(
GenLens[Employee](_.name),
GenLens[Employee](_.company.address.street.name),
GenLens[Employee](_.company.address.city),
GenLens[Employee](_.company.name)
).modify(_.capitalize)
Run Code Online (Sandbox Code Playgroud)
这仍然会copy多次调用.为了提高效率,您可以使用Traversal.apply4et al.品种,这将要求你copy手动编写(我现在懒得做).
最后,如果要将各种转换应用于不同类型的字段,则应该使用modify并set返回类型函数的事实Employee => Employee.对于你的例子,那将是:
val operations = Seq(
GenLens[Employee](_.company.address.street.name).modify(_.capitalize),
GenLens[Employee](_.company.address.street.number).modify(_ + 42),
GenLens[Employee](_.company.name).set("No Company Inc.")
)
val modifyAll = Function.chain(operations)
// does all above operations of course, with two extra copy calls
modifyAll(employee)
Run Code Online (Sandbox Code Playgroud)
* - 这是desugarAmmonite-REPL的简化输出.我跳过了modifyF,顺便说一下
| 归档时间: |
|
| 查看次数: |
397 次 |
| 最近记录: |