Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Construct predicates with lenses

Tags:

haskell

lenses

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

like image 565
ipsec Avatar asked May 13 '13 09:05

ipsec


1 Answers

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
like image 88
shang Avatar answered Oct 20 '22 21:10

shang