2013-07-08 11 views
15

En utilisant une bibliothèque de lentilles je peux appliquer une fonction de modification des objectifs individuels, comme ceci:Combinant lentilles

Prelude Control.Lens> (1, 'a', 2) & _1 %~ (*3) 
(3,'a',2) 
Prelude Control.Lens> (1, 'a', 2) & _3 %~ (*3) 
(1,'a',6) 

Comment puis-je combiner ces lentilles individuelles (_1 et _3) pour être en mesure d'effectuer cette mise à jour aux deux objectifs à la fois? Je me attends quelque chose dans l'esprit de ce qui suit:

Prelude Control.Lens> (1, 'a', 2) & ??? %~ (*3) 
(3,'a',6) 
+0

Je ne suis pas sûr qu'il existe une implémentation judicieuse d'une telle opération. Dites que l'opérateur qui combine vos deux lentilles est '(&&&)'. Il doit avoir un type quelque chose comme '(&&&) :: Lentille a b -> Lentille a b -> Lentille a b' de sorte que vous puissiez l'utiliser de la même manière que les deux lentilles que vous combinez pour le faire. Étant donné que 'view _1 (1,2) = 1' et' view _2 (1,2) = 2', qu'attendez-vous du résultat de 'view (_1 &&& _2) (1,2)'? –

+0

@ChrisTaylor Je n'ai pas vraiment besoin de la fonctionnalité "getter". Bien qu'AFAIU dans cette bibliothèque, il soit classique d'approcher de tels cas avec un monoïde, par ex. le [Traversal] (http://hackage.haskell.org/packages/archive/lens/latest/doc/html/Control-Lens-Traversal.html). –

+2

J'ai lié cela dans les commentaires ci-dessous, mais au cas où quelqu'un le manquerait, il y a un [problème d'objectif] (https://github.com/ekmett/lens/issues/109) sur la combinaison des traversées et les problèmes qu'elles causent. – shachaf

Répondre

19

En utilisant untainted de la, il est possible dans Control.Lens.Internal.Setter classe de type Settable, de combiner deux setters, mais le résultat sera aussi qu'un poseur et non un getter.

import Control.Lens.Internal.Setter 

-- (&&&) is already taken by Control.Arrow 
(~&~) :: (Settable f) => (c -> d -> f a) -> (c -> a -> t) -> c -> d -> t 
(~&~) a b f = b f . untainted . a f 

Vous pouvez tester ceci:

>>> import Control.Lens 
>>> (1, 'a', 2) & (_1 ~&~ _3) %~ (*3) 
(3,'a',6) 

EDIT

Vous n'avez pas réellement besoin d'utiliser les fonctions internes. Vous pouvez utiliser le fait que mutateur est une monade:

{-# LANGUAGE NoMonomorphismRestriction #-} 

import Control.Monad 
import Control.Applicative 

(~&~) = liftA2 (>=>) 

-- This works too, and is maybe easier to understand: 
(~&~) a b f x = a f x >>= b f 
+0

Merci! Existe-t-il un moyen de construire un 'Traversal'? –

+3

Non. Malheureusement [ne fonctionne pas vraiment] (https://github.com/ekmett/lens/issues/109) en général. – shachaf

+1

(_1 ~ & ~ _1) ne satisfait pas les lois Setter (aka SEC). –

7

Il y a une variation sur ce que vous demandez qui est plus générale:

(/\) 
    :: (Functor f) 
    => ((a -> (a, a)) -> (c -> (a, c))) 
    --^Lens' c a 
    -> ((b -> (b, b)) -> (c -> (b, c))) 
    --^Lens' c b 
    -> (((a, b) -> f (a, b)) -> (c -> f c)) 
    --^Lens' c (a, b) 
(lens1 /\ lens2) f c0 = 
    let (a, _) = lens1 (\a_ -> (a_, a_)) c0 
     (b, _) = lens2 (\b_ -> (b_, b_)) c0 
     fab = f (a, b) 
    in fmap (\(a, b) -> 
      let (_, c1) = lens1 (\a_ -> (a_, a)) c0 
       (_, c2) = lens2 (\b_ -> (b_, b)) c1 
      in c2 
      ) fab 

infixl 7 /\ 

se concentrer uniquement sur la signature de type avec des synonymes de type lentille :

Lens' c a -> Lens' c b -> Lens' c (a, b) 

Il prend deux lentilles et les combine dans une lentille à une paire de champs. C'est un peu plus général et fonctionne pour combiner des lentilles qui pointent vers des champs de différents types. Cependant, alors vous devrez muter les deux champs séparément.

Je voulais juste lancer cette solution au cas où les gens chercheraient quelque chose comme ça.

+1

Un type pithier, pour ceux qui leur ressemblent '(/ \) :: LensLike '((,) a) c a -> LensLike' ((,) b) c b -> Lens 'c (a, b)'. Aussi, la restriction évidente, '(/ | \) :: LensLike '((,) a) ca -> LensLike' ((,) a) ca -> Traversale 'ca' avec' a/| \ b = (a/\ b). les deux ». Pourquoi ne sont-ils pas dans 'lens'? –

+0

Réponse immédiate: https://github.com/ekmett/lens/issues/315 –