Создание предикатов с линзами

Я хочу создать функцию A -> Bool, используя некоторые линзы A. Например:

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

l :: [A]

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

Предикат фильтра выглядит немного неуклюжим. ((>100).(^.foo)) не намного лучше. Без линз я бы использовал ((>100) . foo).

Есть ли хороший способ создать такие предикаты с помощью lens? В идеале также разрешались бы такие предикаты, как (\a -> a^.foo > 100 && a^.bar < 50).


person ipsec    schedule 13.05.2013    source источник


Ответы (1)


Я думаю, что ((>100).(^.foo)), вероятно, лучшее, что вы можете сделать, просто используя стандартные операторы. Если вы хотите определить новые операторы сравнения для линз, вы можете сделать что-то вроде:

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

Логика выбора оператора заключается в том, что точка обозначает сторону с линзой, поэтому вы можете написать либо foo .== 5, либо foo .==. bar (где foo и bar — линзы). К сожалению, пакет lens также определяет свой собственный оператор (.<), поэтому, возможно, было бы лучше какое-то другое соглашение об именах. Это была просто первая идея, которая пришла мне в голову.

Используя эти новые операторы, вы сможете писать такие вещи, как

l' = filter (foo .> 100 .&&. bar .< 50) l
person shang    schedule 13.05.2013
comment
Это здорово! Интересно, можно ли каким-то образом использовать классы типов с несколькими параметрами, чтобы заставить работать только › ‹ =? - person Adam Bell; 07.07.2013