module Data.Functor.Selection
(
Selection(..)
, newSelection
, forgetSelection
, select
, include
, exclude
, selectAll
, deselectAll
, invertSelection
, mapSelected
, mapUnselected
, getSelected
, getUnselected
, unify
, trans
, selectWithContext
) where
#if __GLASGOW_HASKELL__ < 710
import Control.Applicative
#endif
import Control.Monad (ap)
import Control.Comonad (Comonad(..))
import Data.Bifoldable (Bifoldable(..))
import Data.Bifunctor (Bifunctor(..))
import Data.Bitraversable (Bitraversable(..))
class Functor f => Selectable s f | s -> f where
modifySelection :: (f (Either b a) -> f (Either d c)) -> s b a -> s d c
modifySelection f = wrapSelection . f . unwrapSelection
wrapSelection :: f (Either b a) -> s b a
unwrapSelection :: s b a -> f (Either b a)
newtype Selection f b a = Selection {
runSelection :: f (Either b a)
} deriving (Functor, Foldable)
deriving instance (Show (f (Either b a))) => Show (Selection f b a)
deriving instance (Eq (f (Either b a))) => Eq (Selection f b a)
instance Monad m => Applicative (Selection m b) where
pure = Selection . pure . Right
(<*>) = ap
instance (Monad m) => Monad (Selection m b) where
return = pure
Selection m >>= k =
Selection $ m >>= either (return . Left) (runSelection . k)
instance (Functor f) => Bifunctor (Selection f) where
first f = Selection . fmap (first f) . runSelection
second = fmap
instance (Foldable f) => Bifoldable (Selection f) where
bifoldMap l r = foldMap (bifoldMap l r) . runSelection
instance (Traversable f) => Bitraversable (Selection f) where
bitraverse l r = fmap Selection . traverse (bitraverse l r) . runSelection
instance (Functor f) => Selectable (Selection f) f where
wrapSelection = Selection
unwrapSelection = runSelection
newSelection :: (Selectable s f) => f a -> s b a
newSelection = wrapSelection . fmap Right
forgetSelection :: (Selectable s f) => s a a -> f a
forgetSelection = unify id id
select :: (Selectable s f) => (a -> Bool) -> s a a -> s a a
select f = include f . deselectAll
include :: (Selectable s f) => (a -> Bool) -> s a a -> s a a
include f = modifySelection (fmap (either (choose f) Right))
exclude :: (Selectable s f) => (a -> Bool) -> s a a -> s a a
exclude f = modifySelection (fmap (either Left (switch . choose f)))
selectAll :: (Selectable s f) => s a a -> s a a
selectAll = include (const True)
deselectAll :: (Selectable s f) => s a a -> s a a
deselectAll = exclude (const True)
invertSelection :: (Selectable s f) => s b a -> s a b
invertSelection = modifySelection (fmap switch)
mapSelected :: (Selectable s f) => (a -> c) -> s b a -> s b c
mapSelected f = modifySelection (fmap (second f))
mapUnselected :: (Selectable s f) => (b -> c) -> s b a -> s c a
mapUnselected f = modifySelection (fmap (first f))
getSelected :: (Selectable s f, Foldable f) => s b a -> [a]
getSelected = foldMap (bifoldMap (const []) pure) . unwrapSelection
getUnselected :: (Selectable s f, Foldable f) => s b a -> [b]
getUnselected = getSelected . invertSelection
unify :: (Selectable s f) => (b -> c) -> (a -> c) -> s b a -> f c
unify l r = fmap (either l r) . unwrapSelection
trans :: (Selectable s f, Selectable t g) => (forall c. f c -> g c) -> s b a -> t b a
trans f = wrapSelection . f . unwrapSelection
selectWithContext :: (Selectable s w, Comonad w) => (w a -> Bool) -> s a a -> s a a
selectWithContext f = modifySelection (extend (choose' extract f) . fmap (either id id))
choose' :: (a -> b) -> (a -> Bool) -> a -> Either b b
choose' f p a = if p a then Right (f a)
else Left (f a)
choose :: (a -> Bool) -> a -> Either a a
choose = choose' id
switch :: Either a b -> Either b a
switch = either Right Left