I’m using Data.Set
s in deeply-nested heterogeneous data structures, and thought it would be helpful to create a Prism
for set membership. Hence:
membership :: (Ord a) => a -> Prism' (Set a) (Set a)
membership a = prism (Set.insert a) g
where g as = if Set.member a as
then Right $ Set.delete a as
else Left as
However, this fails the first prism law, preview l (review l b) ≡ Just b
, in the case where review l
inserts a member that is already present in b
, viz., if l
is the membership lens for a
, and b
is {a}
, then review l b
is also {a}
, and preview l (review l b)
is just the null set, rather than just {a}
as the first prism law requires.
Is there a better optic for capturing set membership? I like being able to check membership and conditionally decompose the set into the matching and non-matching parts simultaneously. Additionally, having an optic to do this is appealing because, since it captures all the use-cases I have for working with Set
s in other parts of my code, it enables me to remove my import Data.Set
statements from the rest of my package, which often indicates successful abstraction to me.
There is Contains
type class with a member:
contains :: Contains m => Index m -> Lens' m Bool
which when specialised to Set
is
contains :: Ord a => a -> Lens' (Set a) Bool
It is probably a good exercise to think why it is a lens (and not a prism, as in your attempt).