Как да комбинирам лещи и функтори?

Опитвам се да свикна с библиотеката lens за Haskell и откривам, че се боря с някои прости проблеми. Например, да кажем (за удобство), че at и _1 имат следните типове (поне така ги разбирам аз):

at :: Ord k => k -> Lens' (Map k v) (Maybe v)

_1 :: Lens' (a, b) a

Как да комбинирам тези лещи в обектив от следния тип:

maybeFst :: Ord k => k -> Lens' (Map k (a, b)) (Maybe a)

person wen    schedule 12.03.2014    source източник


Отговори (1)


Бихте искали обектив като

Lens' (Maybe (a, b)) (Maybe a)

но това не може да е Lens, тъй като връщането на Nothing засяга и b. Може да е Getter

getA :: Getter (Maybe (a, b)) (Maybe a)
getA = to (fmap fst)

но след това, когато го композирате, просто ще завършите с Getter, а не с пълно Lens

maybeFst :: Ord k => k -> Getter (Map k (a, b)) (Maybe a)
maybeFst k = at k . getA

Вероятно по-добре от това е да използвате Traversal вместо това

maybeFstT :: Ord k => k -> Traversal' (Map k (a, b)) a
maybeFstT k = at k . _Just . _1

Това ще ви позволи както да получавате (като използвате preview или toListOf), така и да задавате стойности на fst от стойностите във вашата карта, но няма да можете да промените съществуването й в картата: ако стойността не съществува, не можете да добавите и ако съществува, не можете да го премахнете.


И накрая, можем да монтираме фалшив Lens, който има подходящия тип, въпреки че трябва да му дадем стойност по подразбиране за b

getA :: b -> Lens' (Maybe (a, b)) (Maybe a)
getA b inj Nothing       = (\x -> (,b) <$> x) <$> inj Nothing
getA _ inj (Just (a, b)) = (\x -> (,b) <$> x) <$> inj (Just a)

но забележете, че има някакво не-много-Lensподобно поведение.

>>> Just (1, 2) & getA 0 .~ Nothing & preview (_Just . _2)
Nothing

>>> Nothing & getA 0 .~ Just 1
Just (1,0)

така че често е по-добре да избягвате тези псевдолещи, за да предотвратите злополуки.

person J. Abrahamson    schedule 12.03.2014
comment
Благодаря ви, сега виждам как типът, който питах, никога не можеше да бъде обектив! :) - person wen; 13.03.2014
comment
Като последна бележка, ако имахме Iso' (a, b) a (което очевидно е невъзможно, тогава бихме могли да използваме mapping ourIso :: Iso' (Maybe (a, b)) (Maybe a) вместо getA. По-точно, опитайте fl (a, b) = (b, a) с flipP = iso fl fl и след това maybeFlip k = at k . mapping flipP :: Ord k => k -> Lens' (Map k (a, b)) (Maybe (b, a)). - person J. Abrahamson; 13.03.2014
comment
Само едно малко пояснение: Причината, поради която първият ви пример не може да бъде леща, е, че не можете да измислите b, което да поставите, когато имате Nothing и се опитвате да поставите Just a. Обратният случай работи добре. Ето моя опит: impossible :: Lens' (Maybe (a,b)) (Maybe a); impossible k (Just (a,b)) = fmap (,b) <$> k (Just a); impossible k Nothing = fmap (,undefined) <$> k Nothing. Първо написах impossible k Nothing = Nothing <$ k Nothing, но това не отговаря на законите за лещите. - person Hjulle; 18.01.2018