В вопросе не упоминалось изменение данных, но, когда вам нужно сделать это, вы быстро обнаружите, что в библиотеке Scala нет инструментов, позволяющих сделать это легко (когда данные неизменяемы).Если вы еще этого не испытали, попробуйте написать функцию, которая заменит или изменит value
из Cash
, хранящегося в Person
, используя типы, определенные в вопросе.
Как описано в Tony Morris ' Асимметричные линзы в Scala , линзы являются подходящим решением этой проблемы.
Вот пример того, как мы можем получить доступ и обновить value
человека Cash
с использованием реализаций Lens
и PLens
(частичная линза) из ветви scalaz-seven из Скалаза.
Во-первых, несколько шаблонных: определитеЭкземпляр объектива для каждого поля классов дел.A @-@ B
означает то же самое, что и Lens[A, B]
.
val pants: Person @-@ Option[Pants] =
lensG(_.pants, p => ps => p.copy(pants = ps))
val pocket: Pants @-@ Option[Pocket] =
lensG(_.pocket, ps => p => ps.copy(pocket = p))
val cash: Pocket @-@ Option[Cash] =
lensG(_.cash, p => c => p.copy(cash = c))
val value: Cash @-@ String =
lensG(_.value, c => v => c.copy(value = v))
Однако мы не можем составить все эти объективы, поскольку большинство полей обернуто в Option
типах.
Частичные линзы для спасения: они позволяют нам получать доступ и обновлять части структуры, которые могут не существовать , такие как Some
значение Option
илиhead
из List
.
Мы можем использовать функцию somePLens
из Scalaz 7 для создания частичного объектива, просматривающего каждое дополнительное поле.Однако, чтобы составить частичную линзу с одной из наших обычных линз, нам необходимо получить доступ к эквивалентному частичному экземпляру линзы для обычной линзы, используя метод partial
, который существует на каждом 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))
Таким же образом, мы можем создать нашу частичную линзу, просматривая денежные средства, удерживаемые Person
, путем составления всех экземпляров partial
наших линз и сэндвич-экземпляров somePLens
.Здесь я использовал оператор <=<
, псевдоним для andThen
(что эквивалентно compose
с переключенными операндами).
val someCashValue: Person @-? String =
pants.partial <=< somePLens <=<
pocket.partial <=< somePLens <=<
cash.partial <=< somePLens <=<
value.partial
Создание экземпляра Person
для воспроизведения:
val ben = Person(Some(Pants(Some(Pocket(Some(Cash("zilch")))))))
Использование частичного объектива для получения значения наличных денег:
scala> someCashValue.get(ben)
res2: Option[String] = Some(zilch)
Использование частичного объектива для изменения значения:
scala> someCashValue.mod(_ + ", zero, nada", ben)
res3: Person = Person(Some(Pants(Some(Pocket(Some(Cash(zilch, zero, nada)))))))
Теперь, если я не ношу какие-либо штаны (!), Мы можем видеть, как попытка изменить стоимость моих денег не даст результатов:
scala> val ben = Person(None)
ben: Person = Person(None)
scala> someCashValue.mod(_ + ", zero, nada", ben)
res4: Person = Person(None)