Fre*_*ind 7 scala case-class haskell-lens
一些嵌套的case类和字段addresses
是Seq[Address]
:
// ... means other fields
case class Street(name: String, ...)
case class Address(street: Street, ...)
case class Company(addresses: Seq[Address], ...)
case class Employee(company: Company, ...)
Run Code Online (Sandbox Code Playgroud)
我有一名员工:
val employee = Employee(Company(Seq(
Address(Street("aaa street")),
Address(Street("bbb street")),
Address(Street("bpp street")))))
Run Code Online (Sandbox Code Playgroud)
它有3个地址.
而且我想把街道开头只用"b"开头.我的代码很乱如下:
val modified = employee.copy(company = employee.company.copy(addresses =
employee.company.addresses.map { address =>
address.copy(street = address.street.copy(name = {
if (address.street.name.startsWith("b")) {
address.street.name.capitalize
} else {
address.street.name
}
}))
}))
Run Code Online (Sandbox Code Playgroud)
该modified
员工则:
Employee(Company(List(
Address(Street(aaa street)),
Address(Street(Bbb street)),
Address(Street(Bpp street)))))
Run Code Online (Sandbox Code Playgroud)
我正在寻找一种方法来改进它,但却找不到一种方法.即使尝试过Monocle,也无法将其应用于此问题.
有没有办法让它变得更好?
PS:有两个关键要求:
Tra*_*own 14
正如Peter Neyens指出的那样,Shapeless的SYB在这里工作得非常好,但它会修改树中的所有 Street
值,这可能并不总是你想要的.如果您需要更多控制路径,Monocle可以提供帮助:
import monocle.Traversal
import monocle.function.all._, monocle.macros._, monocle.std.list._
val employeeStreetNameLens: Traversal[Employee, String] =
GenLens[Employee](_.company).composeTraversal(
GenLens[Company](_.addresses)
.composeTraversal(each)
.composeLens(GenLens[Address](_.street))
.composeLens(GenLens[Street](_.name))
)
val capitalizer = employeeStreeNameLens.modify {
case s if s.startsWith("b") => s.capitalize
case s => s
}
Run Code Online (Sandbox Code Playgroud)
正如Julien Truffaut在编辑中指出的那样,你可以通过创建一个镜头一直到街道名称的第一个角色来使这个更简洁(但不那么通用):
import monocle.std.string._
val employeeStreetNameFirstLens: Traversal[Employee, Char] =
GenLens[Employee](_.company.addresses)
.composeTraversal(each)
.composeLens(GenLens[Address](_.street.name))
.composeOptional(headOption)
val capitalizer = employeeStreetNameFirstLens.modify {
case 'b' => 'B'
case s => s
}
Run Code Online (Sandbox Code Playgroud)
有些符号运算符可以使上面的定义更加简洁,但我更喜欢非符号版本.
然后(结果重新格式化以便清晰):
scala> capitalizer(employee)
res3: Employee = Employee(
Company(
List(
Address(Street(aaa street)),
Address(Street(Bbb street)),
Address(Street(Bpp street))
)
)
)
Run Code Online (Sandbox Code Playgroud)
请注意,就像在Shapeless答案中一样,您需要更改您的Employee
定义List
而不是使用Seq
,或者如果您不想更改模型,您可以将该转换构建到Lens
with Iso[Seq[A], List[A]]
.
如果你是开放的更换addresses
中Company
,从Seq
到List
,你可以使用从无形("废你的样板" 例子).
import shapeless._, poly._
case class Street(name: String)
case class Address(street: Street)
case class Company(addresses: List[Address])
case class Employee(company: Company)
val employee = Employee(Company(List(
Address(Street("aaa street")),
Address(Street("bbb street")),
Address(Street("bpp street")))))
Run Code Online (Sandbox Code Playgroud)
Street
如果名称以"b"开头,则可以创建一个多态函数,该函数将a 的名称大写.
object capitalizeStreet extends ->(
(s: Street) => {
val name = if (s.name.startsWith("b")) s.name.capitalize else s.name
Street(name)
}
)
Run Code Online (Sandbox Code Playgroud)
你可以用作:
val afterCapitalize = everywhere(capitalizeStreet)(employee)
// Employee(Company(List(
// Address(Street(aaa street)),
// Address(Street(Bbb street)),
// Address(Street(Bpp street)))))
Run Code Online (Sandbox Code Playgroud)