В прошлой записи мы в общих чертах ознакомились с замечательным пакетом lens, реализующим технику функционального программирования, известную как линзы (обобщённые геттеры и сеттеры).

В пакете определена целая иерархия “разновидностей” линз, откуда и разные названия для их типов: Equality, Iso, Lens, Prism, Getter, Traversal и т.д. Комбинаторы с верхних уровней следующей диаграммы можно применять к типам на нижележащих уровнях:

Иерархия пакета lens

Чтобы лучше овладеть пакетом lens, на протяжении нескольких записей я попробую вкратце сформулировать особенности и цели использования, а также привести содержательные примеры для каждого из типов с диаграммы выше.

Equality

Как я понимаю, линзы Equality являются своего рода “стёклами” (или “оправами”, кому как привычнее): оригинальные данные и данные, полученные в результате “наложения” такого “стекла”, эквивалентны, то есть никакого “искажения” не происходит вовсе. Только единственное “стекло”, тождественное преобразование id, обладает достаточным полиморфизмом:

> 65 ^. id
65
> True & id %~ show
"True"
> pi & id .~ 3.15
3.15

Тип Equality и упрощение Equality' описаны так:

type Equality s t a b = forall p (f :: * -> *) . p a (f b) -> p s (f t)

type Equality' s a = Equality s s a a

-- | Composable `asTypeOf`. Useful for constraining excess polymorphism
type As a = Equality' a a

Последний синоним предназначен для встраивания в цепочку композиций с указанием требуемого на данном этапе конвейра типа:

> True ^. to show . to length
4
it :: Int
> True ^. to show . to length . to fromIntegral . (id :: As Integer)
4
it :: Integer

Говоря языком теории типов, значение типа Equality s t a b является свидетелем (witness) пропозициональной эквивалентности (a ~ s, b ~ t).