Линзы в Haskell: эквивалентности (Equality)
В прошлой записи мы в общих чертах ознакомились с замечательным пакетом lens, реализующим технику функционального программирования, известную как линзы (обобщённые геттеры и сеттеры).
В пакете определена целая иерархия “разновидностей” линз, откуда и разные названия для их типов: Equality
, Iso
, Lens
, Prism
, Getter
,
Traversal
и т.д. Комбинаторы с верхних уровней следующей диаграммы можно применять к типам на нижележащих уровнях:
Чтобы лучше овладеть пакетом 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)
.