линзы для удлинения кортежа? - PullRequest
3 голосов
/ 04 мая 2019

Control.Lens.Tuple определяет линзы для доступа к элементам кортежей. Например

class Field3 s t a b | s -> a, t -> b, s b -> t, t a -> s where
  -- | Access the 3rd field of a tuple.
  _3 :: Lens s t a b

  -- >>> (1,2,3)^._3
  -- 3
  --
  -- >>> _3 .~ "hello" $ (1,2,3)
  -- (1,2,"hello")

Сеттер _3 .~ возвращает кортеж с измененным третьим элементом другого типа в этом примере.

Для класса Field3 есть экземпляры, определенные для кортежей с тремя или более элементами. Не для пар / двух человек, потому что что получит геттер?

Но если установщик может изменить тип n-го элемента, то почему он не может изменить тип на другой набор кортежей?

  -- >>> _3 .~ "hello" $ (1,2)
  -- (1,2,"hello")

То есть установщик _3 .~ устанавливает третий элемент, расширяя его в два раза.

instance Field3 (a,b) (a,b,c') Dummy c' where
  _3 k ~(a,b) = k undefined <&> \c' -> (a,b,c')

, в котором Dummy и undefined - это некоторый тип и значение заполнителя для несуществующего третьего элемента входящего двойника.

После расширения до арности 3 объектив _3 работает как обычно; объективы _1, _2 подчиняются законам об объективах на всем протяжении.

Q1. Будет ли это работать для сеттера?
(конечно, для обновления не будет / over)

Q2. Если да, то есть ли способ сделать его неправильно напечатанным, чтобы попытаться view использовать несуществующий третий элемент?

Q3. Если сеттер не будет работать, может ли быть оператор extend, который достигнет эффекта? (Предположительно с использованием другого функтора.)

Возможно, это может быть случай

instance Field3 (a,b) (a,b,c') (a,b) c' where
  _3 k ~(a,b) = k (a,b) <&> \c' -> (a,b,c')

Так, что view вернет странно типизированный результат, а over потенциально может построить третий элемент из первых двух.

1 Ответ

1 голос
/ 06 мая 2019

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

Допустим, у вас есть это:

bigStructure :: (Int, (Int, (Int, Int)))
bigStructure = (0, (1, (2, 3)))

И вы хотите добавить 10 к самой внутренней паре.Это действие для этой пары, которая является меньшей частью большой структуры.

mk3ple :: c -> (a, b) -> (a, b, c)
mk3ple = flip $ uncurry (,,)

Так что _2 . _2 - это объектив, фокусирующийся на этой самой внутренней паре - другими словами, это функция, которая выполняет действия надэта самая внутренняя пара для действий на большой структуре.Итак, давайте просто ... передадим mk3ple ему (over делает это; он просто позаботится о некоторой Identity занятой работе для нас):

over (_2 . _2) (mk3ple 10) bigStructure

И вот вы - сеттер (концептуальный, то есть не буквальный Setter) для bigStructure, который использует все линзы, которые вам нужны, чтобы сфокусироваться на кортеже.


Конечно, если вы предпочли бы апплет-кортежбудь реальным объективом с геттером и все такое, ты тоже можешь это сделать!Просто представьте единицу как часть каждого кортежа и напишите:

mk3ple' :: Lens (a, b) (a, b, c) () c
mk3ple' = lens (const ()) (uncurry (,,))

set (_2 . _2 . mk3ple') 10 bigStructure
...