Если вы просматриваете запись Lens в хаке, репозитории Lens Github или даже в Google об объекте, вы найдете много частичных ссылок, таких как вводные обучающие материалы/видеоролики, примеры, обзоры и т.д. Поскольку я уже знаю большинство основ, я ищу более полную ссылку, которая поможет мне получить больше знаний о расширенных функциях. Другими словами, я до сих пор не знаю, что это означает и не может найти ресурс, достаточно полный, чтобы объяснить эту графику в целом. Идеи?
Может ли кто-нибудь объяснить диаграмму о библиотеке `lens`?
Ответ 1
Хеддоки - лучший в своем роде ресурс. Они содержат все, но сначала им может быть трудно ориентироваться. Просто просматривайте различные модули и делайте мысленные заметки о том, где и где вы скоро начнете искать свой путь. Диаграмма, на которую вы ссылаетесь, также является очень хорошей картой модулей.
Однако, поскольку вы говорите, что не понимаете графику, я собираюсь предположить, что вам не нужна расширенная или полная ссылка. Диаграмма на самом деле представляет собой очень простой, высокоуровневый обзор частей пакета lens
. Если вы не понимаете диаграмму, вам, вероятно, придется немного подождать с передовым материалом.
Прочитав это, имейте в виду, что, хотя пакет lens
начинался как пакет, ну, линз, в пакете теперь есть много видов оптики, которые подчиняются различным законам и используются для разных вещей. "Оптика" - это общее название объективов, которые вы используете, чтобы ткнуть в структуры данных.
В любом случае, вот как я прочитал диаграмму.
Расположение коробок
Теперь просто взгляните на верхнюю часть ящиков, где записано имя оптики.
-
Верхний уровень,
Fold
иSetter
, являются основной опциейlens
. Как вы можете себе представить, aSetter
является оптикой, которая задает значение некоторого поля. Я собираюсь выпустить кучу примеров здесь, так как вы говорите, что знаете большинство основ, но по существу, когда вы делаетеλ> ellen & age .~ 35 Person { _name = "Ellen", _age = 35 }
то вы использовали
age
какSetter
. -
A
Getter
- это особый видFold
, который позволяет получать значения из структур данных. (Сам AFold
позволяет вам каким-то образом комбинировать значения с новым значением, а не извлекать их, как они есть.) То, что aGetter
является особым типомFold
, помечено на графике стрелкой, указывающей изGetter
доFold
. -
Если вы объедините
Fold
и aSetter
(т.е. вы комбинируете способ циклического набора значений и способ установить одиночные значения), вы получитеTraversal
. ATraversal
- это оптический объект, который нацелен на несколько элементов и позволяет вам устанавливать или изменять их все. -
Далее по диаграмме, если вы объедините
Getter
сTraversal
, вы получитеlens
. Это должно быть вам знакомо, поскольку линзы часто упоминаются как "комбинация геттера и сеттера", и вы можете думать оTraversal
как о более мощномSetter
. -
Я не собираюсь делать вид, что знаю много об отношениях между
Review
,Prism
,Iso
иEquality
. То, как я это понимаю,- A
Review
представляет собой базовую оболочку вокруг функцииb -> t
, гдеb
должен быть полем внутри структурыt
. Таким образом,Review
принимает одно значение поля, а затем создает вокруг него всю структуру. Это может показаться глупым, но на самом деле это обратная сторонаGetter
и полезно для создания призм. - A
Prism
позволяет вам получать и устанавливать значения в типах ветвления. Это кEither
, что alens
для кортежей. Вы не можете получить значение внутриEither
только с объективом, потому что оно взорвется, если оно попадет в ветвьLeft
. - An
Iso
- очень мощная комбинацияlens
иPrism
, которая позволяет вам свободно смотреть "в оба конца" через оптику. В то время какlens
позволяет вам смотреть с высокого уровня на точную часть структуры данных,Iso
позволяет также смотреть с этой точечной части структуры данных на высокий уровень. -
Equality
... Я понятия не имею, извините.
- A
Interlude: Тип подписи
Подпись типа Lens s t a b
может сначала быть пугающей, поэтому я постараюсь быстро покрыть ее. Если вы посмотрите на Getter
, вы увидите, что его подпись типа
Getter s a
Если мы подумаем о том, что представляет собой геттер, это может показаться знакомым. Что такое геттер? Это функция из структуры данных s
до единственного значения a
, которое находится внутри этой структуры. Например, функция Person -> Age
- это геттер, который получает возраст от объекта Person
. Соответствующий Getter
просто имеет сигнатуру
Getter Person Age
Это действительно так просто. A Getter s a
является оптикой, которая может получить a
изнутри s
.
Упрощенная подпись Lens'
должна быть менее страшной. Если вам нужно было догадаться, что такое
Lens' Person Age
? Легко! Это a lens
, которое можно получить и установить (помните, как линзы представляют собой комбинации геттеров и сеттеров?) Поле age
внутри значения Person
. Итак, та же логика, которую вы применили к Getter s a
, можно применить к Lens' s a
.
Но как насчет части s t a b
? Ну, подумайте об этом типе:
data Person a = { _idField :: a, address :: String }
человек идентифицируется по определенной идентификационной величине и имеет возраст. Скажем, вы идентифицированы по имени
carolyn :: Person String
carolyn = Person "Carolyn" "North Street 12"
Что делать, если у вас есть функция toIdNumber :: String -> Int
, которая делает идентификационный номер из строки? Вы можете сделать это:
λ> carolyn & idField %~ toIdNumber
Person 57123 "North Street 12"
но вы не можете, если idField :: Lens' (Person String) String
, потому что этот объектив может иметь дело только с String
s. Он не знает, что значит превратить строку в целое число и вставить ее в одно и то же место. Вот почему у нас Lens s t a b
.
Как я читал эту подпись, "если вы дадите мне функцию a -> b
, я дам вам функцию s -> t
", где a
и b
оба относятся к объекту объектива и s
и t
относятся к содержащим структурам данных. Конкретный пример: если бы мы имели
idField :: Lens (Person String) (Person Int) String Int
мы могли бы сделать преобразование выше. Этот объектив знает, как взять Person
с полем идентификатора строки и превратить его в человека с полем Int
id.
Итак, по существу, a Lens s t a b
можно читать как "функцию" (a -> b) -> (s -> t)
. "Дайте мне преобразование (a -> b)
для моей цели и структуру данных s
, которая содержит эту цель, и я верну вам структуру данных t
, где это преобразование было применено."
Конечно, это не эта функция - она более сильная, чем эта, но вы можете превратить ее в эту функцию, предоставив ее over
, которая является частью Setter
.
Содержимое ящиков
Содержимое ящиков - это просто наиболее распространенные и/или основные операции каждого вида оптики. Их типы много говорят о том, что они делают, и я собираюсь выбрать несколько примеров, чтобы показать, что я имею в виду.
Как правило, самые верхние элементы - это то, как вы создаете такую оптику. Например, самый верхний элемент в поле Getter
- это функция to
, которая строит a Getter
из любой функции s -> a
. Вы получаете Traversal
бесплатно от Traversable
, если используете функцию traverse
.
Тогда ниже это общие операции. Вы, например, найдете view
под Getter
, как вы используете Getter
, чтобы получить что-то из структуры данных. В Setter
вы найдете over
и set
вверх.
(Интересное наблюдение: большинство ящиков, по-видимому, содержат две двойные функции: один способ создания оптики и один способ их использования. Интересно, что у тех, кто часто имеет почти такую же подпись, но перевернуты по сравнению друг с другом. Пример:
-
to :: (s -> a) -> Getter s a
-
view :: Getter s a -> (s -> a)
и
-
unto :: (b -> t) -> Review s t a b
-
review :: Review s t a b -> (b -> t)
Просто что-то смешное я заметил сейчас.)
Как использовать графику
Я обычно не изучаю графику так близко, как я ее пишу. В основном, я просто заглядываю в Setter
, Getter
, Traversal
, lens
и Prism
и где они связаны друг с другом, потому что это наиболее используемая мною оптика.
Когда я знаю, что мне нужно что-то сделать, я обычно быстро заглядываю в графику, чтобы увидеть, какие ящики содержат операции, подобные тем, которые я хочу делать. (Например, если у меня есть data Gendered a = Masculine a | Feminine a
, то _Right
похож на гипотетический _Feminine
.) Когда я определил это, я погружаюсь в Haddocks для этих модулей (в этом примере Prism
), поиск необходимых мне операций.
Как я узнаю об оптике
Я узнаю об оптике и о том, как использовать их так же, как я все узнаю. Я нахожу настоящую проблему, когда оптика является хорошим решением, и я пытаюсь ее решить. Я пробую это, и я терплю неудачу, и я снова попробую, и я прошу о помощи, и я стараюсь, и я стараюсь, и я стараюсь. В конце концов я добьюсь успеха. Затем я попробую что-то немного другое.
Фактически используя вещи так, как они должны были использоваться, я собираю много полезного опыта о том, как они работают и т.д.
Бонус: личное заблуждение
Прежде чем я начал писать это, я подумал, что вам нужно Prism
для обработки значений Maybe
, потому что тип Maybe
является ветвящимся, нет? Не совсем! По крайней мере, не более, чем тип List
, для которого вам не нужен Prism
.
Поскольку тип Maybe
представляет собой контейнер с нулевым или одним элементом, вам действительно нужен только Traversal
, чтобы справиться с ним. Это когда у вас есть две ветки, которые могут содержать разные значения, которые вы начинаете, нуждаются в полной мощности Prism
. (Чтобы построить значение Maybe
, вам все равно нужен Review
.)
Я понял это только тогда, когда начал очень внимательно читать диаграмму и изучать модули, чтобы выяснить фактические формальные различия между оптикой. Это недостаток использования моего метода для изучения вещей. Пока это работает, я просто крыло. Иногда это приводит к окольным путям вещей.
Ответ 2
График - это поток от того, что вы можете сделать со слабой связью между двумя типами, с тем, что вы можете сделать с сильными отношениями.
В самом слабом, вы можете сложить "элементы" типа a
в типе s
, или вы можете установить a
на b
в пределах s
, изменив его на t
(где, конечно, a
и b
могут быть такими же, как при s
и t
). В самом низу у вас есть равенство; один шаг вверх, у вас есть изоморфизм; затем линзы и призмы и т.д.
С другой стороны, он вытекает из наиболее применимых, по крайней мере, применимых, из-за требований отношения: существует множество типов, которые могут быть сложены в терминах некоторого a
концептуально "внутри"; тогда как гораздо меньшее количество будет равно или изоморфно a
.