Подтвердить что ты не робот

Может ли кто-нибудь объяснить диаграмму о библиотеке `lens`?

Если вы просматриваете запись Lens в хаке, репозитории Lens Github или даже в Google об объекте, вы найдете много частичных ссылок, таких как вводные обучающие материалы/видеоролики, примеры, обзоры и т.д. Поскольку я уже знаю большинство основ, я ищу более полную ссылку, которая поможет мне получить больше знаний о расширенных функциях. Другими словами, я до сих пор не знаю, что это означает и не может найти ресурс, достаточно полный, чтобы объяснить эту графику в целом. Идеи?

4b9b3361

Ответ 1

Хеддоки - лучший в своем роде ресурс. Они содержат все, но сначала им может быть трудно ориентироваться. Просто просматривайте различные модули и делайте мысленные заметки о том, где и где вы скоро начнете искать свой путь. Диаграмма, на которую вы ссылаетесь, также является очень хорошей картой модулей.

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

Прочитав это, имейте в виду, что, хотя пакет lens начинался как пакет, ну, линз, в пакете теперь есть много видов оптики, которые подчиняются различным законам и используются для разных вещей. "Оптика" - это общее название объективов, которые вы используете, чтобы ткнуть в структуры данных.

В любом случае, вот как я прочитал диаграмму.

Расположение коробок

Теперь просто взгляните на верхнюю часть ящиков, где записано имя оптики.

  • Верхний уровень, Fold и Setter, являются основной опцией lens. Как вы можете себе представить, a Setter является оптикой, которая задает значение некоторого поля. Я собираюсь выпустить кучу примеров здесь, так как вы говорите, что знаете большинство основ, но по существу, когда вы делаете

    λ> ellen & age .~ 35
    Person { _name = "Ellen", _age = 35 }
    

    то вы использовали age как Setter.

  • A Getter - это особый вид Fold, который позволяет получать значения из структур данных. (Сам A Fold позволяет вам каким-то образом комбинировать значения с новым значением, а не извлекать их, как они есть.) То, что a Getter является особым типом Fold, помечено на графике стрелкой, указывающей из Getter до Fold.

  • Если вы объедините Fold и a Setter (т.е. вы комбинируете способ циклического набора значений и способ установить одиночные значения), вы получите Traversal. A Traversal - это оптический объект, который нацелен на несколько элементов и позволяет вам устанавливать или изменять их все.

  • Далее по диаграмме, если вы объедините Getter с Traversal, вы получите lens. Это должно быть вам знакомо, поскольку линзы часто упоминаются как "комбинация геттера и сеттера", и вы можете думать о Traversal как о более мощном Setter.

  • Я не собираюсь делать вид, что знаю много об отношениях между Review, Prism, Iso и Equality. То, как я это понимаю,

    • A Review представляет собой базовую оболочку вокруг функции b -> t, где b должен быть полем внутри структуры t. Таким образом, Review принимает одно значение поля, а затем создает вокруг него всю структуру. Это может показаться глупым, но на самом деле это обратная сторона Getter и полезно для создания призм.
    • A Prism позволяет вам получать и устанавливать значения в типах ветвления. Это к Either, что a lens для кортежей. Вы не можете получить значение внутри Either только с объективом, потому что оно взорвется, если оно попадет в ветвь Left.
    • An Iso - очень мощная комбинация lens и Prism, которая позволяет вам свободно смотреть "в оба конца" через оптику. В то время как lens позволяет вам смотреть с высокого уровня на точную часть структуры данных, Iso позволяет также смотреть с этой точечной части структуры данных на высокий уровень.
    • Equality... Я понятия не имею, извините.

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.