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

В чем разница между линзами и молниями?

Это пример использования застежки-молнии в Haskell:

data Tree a = Fork (Tree a) (Tree a) | Leaf a
data Cxt a = Top | L (Cxt a) (Tree a) | R (Tree a) (Cxt a)
type Loc a = (Tree a, Cxt a)

left :: Loc a -> Loc a
left (Fork l r, c) = (l, L c r)

right :: Loc a -> Loc a
right (Fork l r, c) = (r, R l c)

top :: Tree a -> Loc a 
top t = (t, Top)

up :: Loc a -> Loc a
up (t, L c r) = (Fork t r, c)
up (t, R l c) = (Fork l t, c)

upmost :: Loc a -> Loc a
upmost [email protected](t, Top) = l
upmost l = upmost (up l)

modify :: Loc a -> (Tree a -> Tree a) -> Loc a
modify (t, c) f = (f t, c)

Это пример использования застежки-молнии в Clojure:

(use 'clojure.zip)
(require '[clojure.zip :as z])

user> (def z [[1 2 3] [4 [5 6] 7] [8 9]])
#'user/z

user> (def zp (zipper vector? seq (fn [_ c] c) z))
#'user/zp

user> zp
[[[1 2 3] [4 [5 6] 7] [8 9]] nil]

user> (-> zp down)
[[1 2 3] {:l [], :pnodes [[[1 2 3] [4 [5 6] 7] [8 9]]], :ppath nil, :r ([4 [5 6] 7] [8 9])}]

user> (first (-> zp down))
[1 2 3]

Это пример использования объектива в Haskell:

data Person = P { name :: String 
                , addr :: Address 
                }
data Address = A { street :: String
                 , city :: String
                 , postcode :: String 
                 }

setPostcode :: String -> Person -> Person
setPostcode pc p = p { addr = addr p { postcode = pc }}

Это пример использования объектива в Clojure.

(use 'lens)

(defrecord Address [street city postcode])
(defrecord Person [name age address])
(defrecord User [uid username identity password])

(def -postcode (mklens :postcode))
(def -city (mklens :city))
(def -street (mklens :street))
(def -address (mklens :address))
(def -age (mklens :age))
(def -name (mklens :name))
(def -uid (mklens :uid))
(def -username (mklens :username))
(def -identity (mklens :identity))
(def -password (mklens :password))

(-get -postcode home)

(-set -postcode home 500)

Теперь кажется, что оба объектива и молнии являются функциональными способами прохождения вложенных структур данных.

Мой вопрос: Каковы различия между объективами и молниями? Подходит ли он к конкретному варианту использования?

4b9b3361

Ответ 1

Молнии сродни курсорам: они позволяют перемещаться по деревьям упорядоченным образом. Их обычные операции: up, down, left, right и edit. (имена могут различаться в зависимости от имплантата)

Объективы - это своего рода обобщенные ключи (как в "ключах ассоциативной структуры данных" ). Структуру не нужно заказывать. Их обычные операции fetch и putback и очень похожи на get и assoc. (имена могут различаться в зависимости от имплантата)

Итак, как вы видите, молнии очень обеспокоены иерархией (вверх/вниз) и порядком (влево/вправо), в то время как объективы просто фокусируются (отсюда и название) на части данных, что может быть даже проекцией ( это то, чего не существовало само по себе в исходной структуре).

Например, в моей текущей работе над Enliven у меня есть объективы, которые позволяют мне сосредоточиться на одном атрибуте класса или стиля в документе HTML.

Ответ 2

Молнии - это вариант типа данных, который разворачивает тип в его локальный контекст и его экстенты во всех направлениях. На вершине Zipper вы можете реализовать эффективное движение и локальное обновление.

Линзы - это экзамены первого класса для конкретного компонента типа данных. Они сосредоточены на 0, 1 или многих частях структуры данных. Примечательно, что ваш пример объектива в Haskell на самом деле не является объективом - это не первый класс.

Совершенно разумно создать объектив, который фокусируется на некоторой части молнии. Например, даже более простая застежка-молния, чем ваши примеры, - это застежка-молния Cons,

data Cons a = Empty | Cons a (Cons a)

data ConsZ a = ConsZ { lefts :: Cons a; here :: a; rights :: Cons a }

zip :: Cons a -> Maybe (ConsZ a)
zip Empty = Nothing
zip (Cons a as) = ConsZ Empty a as

unzip :: ConsZ a -> Cons a
unzip (ConsZ Empty a as) = Cons a as
unzip (ConsZ (Cons l ls) a as) = unzip (ConsZ ls) l (Cons a as)

Мы можем постепенно изменять эту структуру, перемещая фокус влево или вправо

moveRight :: ConsZ a -> Maybe (ConsZ a)
moveRight (ConsZ _ _ Empty) = Nothing
moveRight (ConsZ ls x (Cons a as)) =  ConsZ (Cons x ls) a as

и измените текущую локальную точку

modify :: (a -> a) -> ConsZ a -> ConsZ a
modify f (ConsZ ls x rs) = ConsZ ls (f x) rs

Мы также можем создавать объективы, которые обращаются к каждой части структуры застежки-молнии

type Lens s a = forall f . Functor f => (a -> f a) -> (s -> f s)

_lefts :: Lens (ConsZ a) a
_lenfs inj (ConsZ ls x rs) = (\ls -> ConsZ ls' x rs) <$> inj ls

_here :: Lens (ConsZ a) a
_here inj (ConsZ ls x rs) = (\x' -> ConsZ ls x' rs) <$> inj x

И даже использовать их для эффективного создания наших операций с молнией

over :: ((a -> Identity a) -> s -> Identity s) -> (a -> a) -> (s -> s)
over l f s = runIdentity (l (Identity . f) s)

modify = over _here

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