gpt4 book ai didi

scala - 结合镜片的集合

转载 作者:行者123 更新时间:2023-12-04 11:19:24 25 4
gpt4 key购买 nike

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)

太好了据我了解,宏魔术将所有内容完全转换为与上述相同的代码。

但是,如果我想结合几个 Action 怎么办?如果我想通过一个电话同时更改街道名称,地址城市和公司名称怎么办?如下所示:
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}
)

或类似的东西...

最佳答案

As far as I understand, macros magic converts everything exactly to the same code as above.



没有。

这个简单的代码:
employee.lens(_.name)
.modify(_.capitalize)

变成那种怪异的东西*:
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)

这远非简单
employee.copy(name = employee.name.capitalize)

并包含三个冗余对象(匿名镜头类,用于语法糖的ApplyLens和从 modify返回的lambda)。通过直接使用 capitalize而不是与 headOption组成,我们跳过了更多内容。

因此,没有免费的晚餐。但是,在大多数情况下,它已经足够好了,没有人关心多余的镜头对象和中间结果。

多种操作

如果它们的类型对齐,则可以从多个镜头构建遍历(集合镜头)(这里是 EmployeeString)
val capitalizeAllFields = Traversal.applyN(
GenLens[Employee](_.name),
GenLens[Employee](_.company.address.street.name),
GenLens[Employee](_.company.address.city),
GenLens[Employee](_.company.name)
).modify(_.capitalize)

这仍然会多次调用 copy。为了提高效率,您可以使用 Traversal.apply4等。品种,这将需要您手动编写 copy(而且我现在很懒惰,无法这样做)。

最后,如果要将各种转换应用于不同类型的字段,则应该使用 modifyset返回 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)

*-这是Ammonite-REPL中 desugar的简化输出。我跳过了 modifyF,顺便说一句

关于scala - 结合镜片的集合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45714219/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com