2013-05-13 14 views
6

Chcę utworzyć funkcję A -> Bool korzystania z niektórych obiektywów A. Na przykład:Construct predykatów z obiektywami

data A = A { _foo :: Int, _bar :: Int } 
makeLenses ''A 

l :: [A] 

l' = filter (\a -> a^.foo > 100) l 

Predykat filtr wygląda nieco clumpsy. ((>100).(^.foo)) nie jest dużo lepszy. Bez soczewek użyłbym ((>100) . foo).

Czy istnieje dobry sposób na tworzenie takich predykatów z lens? Idealnie byłoby także zezwolić na predykaty takie jak (\a -> a^.foo > 100 && a^.bar < 50).

Odpowiedz

4

Myślę, że ((>100).(^.foo)) jest prawdopodobnie najlepszym rozwiązaniem dla zwykłych operatorów. Jeśli są chętni do definiowania nowych operatorów porównania do obiektywów, można zrobić coś takiego:

import Control.Lens hiding ((.>)) 
import Control.Monad  (liftM2) 
import Control.Monad.Reader (MonadReader) 
import Data.Function  (on) 

(.==) :: (MonadReader s m, Eq a) => Getting Bool s a -> a -> m Bool 
(.==) l = views l . (==) 
infix 4 .== 

(.==.) :: (MonadReader s m, Eq a) => Getting a s a -> Getting a s a -> m Bool 
(.==.) = liftM2 (==) `on` view 
infix 4 .==. 

(.<) :: (MonadReader s m, Ord a) => Getting Bool s a -> a -> m Bool 
(.<) l = views l . flip (<) 
infix 4 .< 

(.<.) :: (MonadReader s m, Ord a) => Getting a s a -> Getting a s a -> m Bool 
(.<.) = liftM2 (<) `on` view 
infix 4 .<. 

(.<=) :: (MonadReader s m, Ord a) => Getting Bool s a -> a -> m Bool 
(.<=) l = views l . flip (<=) 
infix 4 .<= 

(.<=.) :: (MonadReader s m, Ord a) => Getting a s a -> Getting a s a -> m Bool 
(.<=.) = liftM2 (<=) `on` view 
infix 4 .<=. 


(.>) :: (MonadReader s m, Ord a) => Getting Bool s a -> a -> m Bool 
(.>) l = views l . flip (>) 
infix 4 .> 

(.>.) :: (MonadReader s m, Ord a) => Getting a s a -> Getting a s a -> m Bool 
(.>.) = liftM2 (>) `on` view 
infix 4 .>. 

(.>=) :: (MonadReader s m, Ord a) => Getting Bool s a -> a -> m Bool 
(.>=) l = views l . flip (>=) 
infix 4 .>= 

(.>=.) :: (MonadReader s m, Ord a) => Getting a s a -> Getting a s a -> m Bool 
(.>=.) = liftM2 (>=) `on` view 
infix 4 .>=. 

(.&&.) :: Monad m => m Bool -> m Bool -> m Bool 
(.&&.) = liftM2 (&&) 
infix 3 .&&. 

(.||.) :: Monad m => m Bool -> m Bool -> m Bool 
(.||.) = liftM2 (||) 
infix 3 .||. 

logika wyboru operatora jest to, że kropka oznacza stronę, która ma obiektyw, więc można napisać albo foo .== 5 lub foo .==. bar (gdzie foo i bar są soczewkami). Niestety, pakiet lens definiuje również swój własny operator (.<), więc może jakaś inna konwencja nazewnictwa byłaby lepsza. To był tylko pierwszy pomysł, który przyszedł mi do głowy.

Korzystając z tych nowych operatorów, byłbyś w stanie napisać takie rzeczy

l' = filter (foo .> 100 .&&. bar .< 50) l 
+0

To jest świetne! Zastanawiam się, czy w jakimś stopniu użyć klas typu wieloparametrowego do wykonania tylko><= pracy? –

Powiązane problemy