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)
.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With