Câu hỏi đặt ra đã không đề cập sửa đổi dữ liệu, nhưng khi bạn cần làm điều này bạn nhanh chóng tìm thấy thư viện Scala không có các công cụ để thực hiện điều này dễ dàng (khi dữ liệu là không thay đổi). Nếu bạn chưa trải nghiệm điều này, hãy thử viết một hàm sẽ thay thế hoặc sửa đổi, value
của Cash
được giữ bởi một Person
, sử dụng các loại được xác định trong câu hỏi.
Như được mô tả trong ống kính Tony Morris 'Asymmetric Lenses in Scala, là giải pháp thích hợp cho vấn đề này.
Dưới đây là một ví dụ về cách chúng ta có thể truy cập và cập nhật các value
của Cash
sử dụng Lens
và PLens
(ống kính một phần) triển khai của một người từ scalaz-seven chi nhánh của Scalaz.
Đầu tiên, một số bản mẫu: xác định phiên bản Lens cho từng trường của các lớp chữ hoa chữ thường. A @[email protected] B
có nghĩa là Lens[A, B]
.
val pants: Person @[email protected] Option[Pants] =
lensG(_.pants, p => ps => p.copy(pants = ps))
val pocket: Pants @[email protected] Option[Pocket] =
lensG(_.pocket, ps => p => ps.copy(pocket = p))
val cash: Pocket @[email protected] Option[Cash] =
lensG(_.cash, p => c => p.copy(cash = c))
val value: Cash @[email protected] String =
lensG(_.value, c => v => c.copy(value = v))
Tuy nhiên, chúng tôi không thể tạo tất cả các ống kính này vì hầu hết các trường được bao bọc trong các loại Option
.
Ống kính phần để giải cứu: những cho phép chúng ta truy cập và cập nhật các phần của một cấu trúc mà có thể không tồn tại, chẳng hạn như giá trị của một Some
Option
, hoặc head
của một List
.
Chúng tôi có thể sử dụng chức năng somePLens
từ Scalaz 7 để tạo ống kính một phần xem từng trường tùy chọn.Tuy nhiên, để tạo một thấu kính một phần với một trong các ống kính thông thường của chúng tôi, chúng tôi cần truy cập vào trường hợp ống kính một phần tương đương cho ống kính thông thường, sử dụng phương pháp partial
tồn tại trên mọi Lens
.
// @-? is an infix type alias for PLens
val someCash: Pocket @-? Cash = cash.partial andThen somePLens
scala> someCash.get(Pocket(Some(Cash("zilch"))))
res1: Option[Cash] = Some(Cash(zilch))
Trong cùng một cách, chúng ta có thể tạo ra ống kính một phần của chúng tôi xem tiền mặt nắm giữ bởi một Person
bằng cách soạn bài partial
trường hợp ống kính của chúng tôi, và kẹp trường hợp của somePLens
. Ở đây, tôi đã sử dụng toán tử <=<
, một bí danh cho andThen
(tương đương với compose
với các toán hạng được bật).
val someCashValue: Person @-? String =
pants.partial <=< somePLens <=<
pocket.partial <=< somePLens <=<
cash.partial <=< somePLens <=<
value.partial
Tạo một trường hợp Person
để chơi với:
val ben = Person(Some(Pants(Some(Pocket(Some(Cash("zilch")))))))
Sử dụng ống kính một phần để truy cập giá trị tiền mặt tôi có:
scala> someCashValue.get(ben)
res2: Option[String] = Some(zilch)
Sử dụng ống kính một phần để thay đổi giá trị :
scala> someCashValue.mod(_ + ", zero, nada", ben)
res3: Person = Person(Some(Pants(Some(Pocket(Some(Cash(zilch, zero, nada)))))))
(!) 0
Bây giờ, nếu tôi không mặc bất kỳ quần, chúng ta có thể xem như thế nào là một nỗ lực để thay đổi giá trị tiền mặt của tôi sẽ không có hiệu lực:
scala> val ben = Person(None)
ben: Person = Person(None)
scala> someCashValue.mod(_ + ", zero, nada", ben)
res4: Person = Person(None)
Tuyệt vời, cảm ơn! Cách tiếp cận hiểu được thực sự là chính xác những gì tôi đang cố gắng làm, nhưng cấu trúc tôi đang làm việc không sạch như ví dụ tôi đã đưa ra ở trên. Ít nhất điều này xác nhận tôi đang đi đúng hướng. –