Para compreender profundamente o funcionamento da biblioteca lens em Haskell, é essencial observar como as funções fundamentais se expandem durante a avaliação. Abaixo, exploramos as definições básicas e realizamos a expansão manual de operações comuns.
Definições Base e Operações Fundamentais
As funções view, over e set são construídas sobre os conceitos de Const e Identity. Aqui estão suas implementações simplificadas:
-- Recupera um valor através de uma lente
view lensFn = getConst . lensFn Const
-- Aplica uma função sobre o alvo da lente
over lensFn f = runIdentity . lensFn (Identity . f)
-- Define um valor estático
set lensFn val = runIdentity . lensFn (\_ -> Identity val)
-- Construtor para getters customizados
to k = dimap k (contramap k)
-- Exemplo de implementação do getter para o primeiro elemento de uma tupla
instance Field1 (x, y) (x', y) x x' where
_1 optic ~(val1, val2) = optic val1 <$> \newVal -> (newVal, val2)
Expansão de Operações com Tuplas
Vamos rastrear como o Haskell processa a operação view _1 (10, 20):
view _1 (10, 20)
= getConst . _1 Const $ (10, 20)
= getConst $ _1 Const (10, 20)
= getConst $ Const 10 <$> \newVal -> (newVal, 20)
= getConst $ Const 10
= 10
Agora, o processo para over alterando o estado:
over _1 (+5) (10, 20)
= runIdentity . _1 (Identity . (+5)) $ (10, 20)
= runIdentity $ _1 (Identity . (+5)) (10, 20)
= runIdentity $ (Identity . (+5)) 10 <$> \newVal -> (newVal, 20)
= runIdentity $ Identity 15 <$> \newVal -> (newVal, 20)
= runIdentity $ Identity (15, 20)
= (15, 20)
Composição e Transformação com 'to'
A composição de lentes permite transformações complexas. Veja a expansão de view (_1 . to abs) (-5, 10):
view (_1 . to abs) (-5, 10)
= getConst $ (_1 . to abs) Const (-5, 10)
= getConst $ _1 (to abs Const) (-5, 10)
= getConst $ (to abs Const) (-5) <$> \newVal -> (newVal, 10)
= getConst $ (dimap abs (contramap abs) (\x -> Const x)) (-5) <$> \newVal -> (newVal, 10)
= getConst $ ((contramap abs) . (\x -> Const x) . abs $ (-5)) <$> \newVal -> (newVal, 10)
= getConst $ ((contramap abs) . (\x -> Const x) $ 5) <$> \newVal -> (newVal, 10)
= getConst $ (contramap abs $ Const 5) <$> \newVal -> (newVal, 10)
= getConst $ Const 5 <$> \newVal -> (newVal, 10)
= 5
Estruturas de Suporte: Const, Profunctor e Identity
O funcionamento das lenses depende de instânccias específicas de Functors e Profunctors:
-- Const armazena um valor sem transformá-lo via fmap
newtype Const a b = Const { getConst :: a }
instance Functor (Const m) where
fmap _ (Const v) = Const v
-- Identity permite a reconstrução da estrutura
newtype Identity a = Identity { runIdentity :: a }
instance Functor Identity where
fmap f m = Identity (f (runIdentity m))
-- Funções nativas como Profunctors
instance Profunctor (->) where
dimap ab cd bc = cd . bc . ab
Análise de Prisms: preview _Left
Prisms lidam com estruturas que podem ou não conter um valor. Veja como preview funciona internamente:
_Left = prism Left $ either Right (Left . Right)
prism bt seta = dimap seta (either pure (fmap bt)) . right'
preview l = getFirst . foldMapOf l (First . Just)
foldMapOf l f = getConst . l (Const . f)
Cálculo manual simplificado de preview _Left (Left 100):
preview _Left (Left 100)
= getFirst $ getConst $ _Left (Const . First . Just) (Left 100)
= getFirst $ getConst $ (dimap (either Right (Left . Right)) (either pure (fmap Left)) . right') (Const . First . Just) (Left 100)
= getFirst $ getConst $ (either pure (fmap Left)) $ fmap (Const . First . Just) (Right 100)
= getFirst $ getConst $ Right (Const . First $ Just 100)
= Just 100
Manipulação de Coleções com 'mapped'
O uso de mapped permite aplicar alterações em todos os elementos de um container de forma genérica através da classe Settable:
set mapped 9 [1, 2, 3]
= runIdentity $ (sets fmap) (\_ -> Identity 9) [1, 2, 3]
= runIdentity $ Identity . (fmap (\_ -> 9)) $ [1, 2, 3]
= runIdentity $ Identity [9, 9, 9]
= [9, 9, 9]
Extração com 'toListOf' e 'both'
Para estruturas que suportam Bitraversable, como tuplas, o combinader both permite extrair múltiplos valores simultaneamente:
toListOf both ("A", "B")
= flip appEndo [] . getConst $ bitraverse (Const . Endo . (:)) (Const . Endo . (:)) ("A", "B")
= flip appEndo [] . getConst $ (,) <$> (Const (Endo ("A":))) <*> (Const (Endo ("B":)))
= flip appEndo [] . getConst $ (Const (Endo (("A":) . ("B":))))
= appEndo (Endo (("A":) . ("B":))) []
= ["A", "B"]