Простая интуиция линз, призм, изоморфизмов (lens)
Введение⌗
В данной заметке я вкратце и упрощённо поделюсь своим пониманием общей картины (“Big Picture”) библиотеки lens
и
вытекающей из него интуиции о линзах.
Интуиция основных операций с линзовыми объектами⌗
Итак, имея линзовый объект (о них - позже), мы можем осуществлять с его помощью четыре фундаментальных действия:
- Смотреть на фокус линзы, то есть фокусироваться внутрь структуры данных:
view
- Модифицировать данные в фокусе линзы, оставляя остальную структуру без изменений:
over
- Полностью замещать данные в фокусе линзы (может быть, на другой тип), оставляя остальную структуру без
изменений:
set
- Комбинировать линзовые объекты друг с другом, производя новый линзовый объект:
(.)
Примеры⌗
Пусть даны типы:
data Sex = Male | Female
data Person = Person
{ _sex :: Sex
, _age :: Int
, _siblings :: [Person]
}
-- автогенерация линзовых объектов для типов выше
makePrisms ''Sex
makeLenses ''Person
s1 = Male
p1 = Person Male 28 []
p2 = Person Female 30 [Person Male 14 [], Person Female 3 [p1]]
Попробуем применить к нему несколько операций (первыми параметрами всегда стоят линзовые объекты):
> view sex p1
Male
> over age (+1) p1
Person {_sex = Male, _age = 29, _siblings = []}
> set sex Female p1
Person {_sex = Female, _age = 28, _siblings = []}
> -- используя другие линзы библиотеки
> toListOf (siblings . traverse . sex) p2
[Male,Female]
> let p2' = over (siblings . mapped . age) (+100) p2
> let p2'' = over (siblings . mapped . siblings . mapped . age) (+1000) p2
> :force p2'
p2' = Person
Female 30
[Person Male 114 [],Person Female 103 [Person Male 28 []]]
> :force p2''
p2'' = Person
Female 30 [Person Male 14 [],Person Female 3 [Person Male 1028 []]]
В двух последних примерах мы удобно “состарили” прямых и непрямых родственников госпожи p2
.
Интуиция основных линзовых объектов⌗
Упрощённо говоря, линзовые объекты бывают следующих видов:
- Изоморфизмы
Iso
– биекции между типами, например изоморфизм между(Maybe a)
и(Either () a)
. Изоморфизмы могут быть между любыми типами, имеющими одинаковую форму: например, при наличии изоморфизма между полом человека и булевым типом, мы можем сменить пол всем родственникам:over (siblings . traverse . sex) (view (sexBoolIso . to not . from sexBoolIso)) p2
. Я бы сказал, чтоIso
являются разными отражениями одного и того же объекта. - Линзы
Lens
– фокус внутрь структуры (на часть целого). Линзы существуют только для типов-произведений (декартовых пар,Person
и т.д.), поскольку в фокусе линзы составная (интересующая нас) часть структуры находится всегда. Я бы назвал линзу фокусом на проекцию. - Призмы
Prism
– фокус на поддерево (вариант, слагаемое суммы) структуры. Призмы существуют только для типов-сумм (типов-перечислений вроде чисел,Bool
, списков и т.д.). Фокус призмы, в отличии от линзы, может оказаться “мимо” данных: например фокус на слагаемоеTrue
в типеBool
зафейлится на конкретной структуреFalse :: Bool
. Я бы назвал призму фокусом на вариант. - Перевёрнутые призмы
Review
– фокуc на остальную часть структуры, находящуюся вне фокусаPrism
(соответственно, можно восстановить целиком структуру по перевёрнутой призме). Получаются из некоторых призм их переворачиванием (re
). Я бы назвал их фокусом на дополнение (complement, теория множеств) - Фолды (Folds) и Траверсалы (Traversals) – фокус на несколько элеменов одновременно (мы можем и не знать, на сколько).
Кстати, в библиотеке lens
для многих типов определены синонимы с сильно упрощёнными сигнатурами - их имена заканчиваются “штрихом”:
> :t Iso
Iso :: Iso s t a b -> ReifiedIso s t a b
> :info Iso'
type Iso' s a = Iso s s a a -- Defined in ‘Control.Lens.Type’
Пока всё об интуиции.
ЧТАТ ДРУГ ЗПСИ