Construct predicates with lenses
Asked Answered
J

1

7

I want to create a function A -> Bool using some lenses of A. For instance:

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

l :: [A]

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

The filter predicate looks a bit clumpsy. ((>100).(^.foo)) is not much better. Without lenses, I would use ((>100) . foo).

Is there a nice way to create such predicates with lens? Ideally it would also allow predicates like (\a -> a^.foo > 100 && a^.bar < 50).

Joses answered 13/5, 2013 at 9:54 Comment(0)
M
4

I think ((>100).(^.foo)) is probably the best you can do with just using the standard operators. If you are willing to define new comparison operators for lenses, you could do something like:

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 .||.

The logic behind the operator choices is that the dot signifies the side that has a lens, so you could write either foo .== 5 or foo .==. bar (where foo and bar are lenses). Unfortunately, the lens package also defines its own (.<) operator, so maybe some other naming convention would be better. This was just the first idea that came to my mind.

Using these new operators, you would be able to write stuff like

l' = filter (foo .> 100 .&&. bar .< 50) l
Mefford answered 13/5, 2013 at 10:36 Comment(1)
This is great! I wonder if there someway to use multi-parameter type classes to make just > < = work?Zabrina

© 2022 - 2024 — McMap. All rights reserved.