Monocle は、レンズ パターンを実装する優れたライブラリ (唯一のライブラリではありません) です。これは、巨大なネストされたオブジェクトの 1 つのフィールドを変更する必要がある場合に最適です。例のように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)
これは素晴らしいことです。私の知る限り、マクロ マジックはすべてを上記とまったく同じコードに変換します。
しかし、複数のアクションを組み合わせたい場合はどうすればよいでしょうか? 1回の電話で通りの名前、住所の都市、会社名を同時に変更したい場合はどうすればよいですか? 次のように:
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)
これは、最終的にONEだけでなく、 THREE employee.copy(...).copy(...).copy(...)
呼び出しに変換されます。それをより良くする方法は? employee.copy(...)
さらに、一連の操作を適用することは本当に素晴らしいことです。Seq[(Lens[Employee, String], String => String)]
最初の要素が正しいフィールドを指すレンズであり、2 番目の要素がそれを変更する関数であるペアのシーケンスと同様です。このような一連の操作を外部から構築するのに役立ちます。上記の例の場合:
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}
)
または似たようなもの...