module Control.Compactable
( Compactable (..)
, fforMaybe
, fforEither
, fmapMaybeM
, fmapEitherM
, fforMaybeM
, fforEitherM
, applyMaybeM
, bindMaybeM
, traverseMaybeM
, altDefaultCompact
, altDefaultSeparate ) where
import Control.Applicative
import Control.Monad (join)
import Control.Monad.Trans.Except
import Control.Monad.Trans.Maybe
import Data.Bifunctor (bimap)
import Data.Either (partitionEithers)
import Data.Foldable (foldl')
import Data.Functor.Compose
import qualified Data.Functor.Product as FP
import qualified Data.IntMap as IntMap
import qualified Data.Map as Map
import Data.Maybe
import Data.Proxy
import Data.Semigroup
import qualified Data.Sequence as Seq
import qualified Data.Set as Set
import qualified Data.Vector as V
import GHC.Conc
import Text.ParserCombinators.ReadP
import Text.ParserCombinators.ReadPrec
class Compactable (f :: * -> *) where
compact :: f (Maybe a) -> f a
default compact :: Functor f => f (Maybe a) -> f a
compact = snd . separate . fmap (\case Just x -> Right x; _ -> Left ())
separate :: f (Either l r) -> (f l, f r)
default separate :: Functor f => f (Either l r) -> (f l, f r)
separate xs = (compact $ hush . flipEither <$> xs, compact $ hush <$> xs)
filter :: (a -> Bool) -> f a -> f a
default filter :: Functor f => (a -> Bool) -> f a -> f a
filter f = fmapMaybe $ \a -> if f a then Just a else Nothing
fmapMaybe :: Functor f => (a -> Maybe b) -> f a -> f b
fmapMaybe f = compact . fmap f
fmapEither :: Functor f => (a -> Either l r) -> f a -> (f l, f r)
fmapEither f = separate . fmap f
applyMaybe :: Applicative f => f (a -> Maybe b) -> f a -> f b
applyMaybe fa = compact . (fa <*>)
applyEither :: Applicative f => f (a -> Either l r) -> f a -> (f l, f r)
applyEither fa = separate . (fa <*>)
bindMaybe :: Monad f => f a -> (a -> f (Maybe b)) -> f b
bindMaybe x = compact . (x >>=)
bindEither :: Monad f => f a -> (a -> f (Either l r)) -> (f l, f r)
bindEither x = separate . (x >>=)
traverseMaybe :: (Applicative g, Traversable f)
=> (a -> g (Maybe b)) -> f a -> g (f b)
traverseMaybe f = fmap compact . traverse f
traverseEither :: (Applicative g, Traversable f)
=> (a -> g (Either l r)) -> f a -> g (f l, f r)
traverseEither f = fmap separate . traverse f
instance Compactable Maybe where
compact = join
fmapMaybe = (=<<)
separate = \case
Just x -> case x of
Left l -> (Just l, Nothing)
Right r -> (Nothing, Just r)
_ -> (Nothing, Nothing)
instance Monoid m => Compactable (Either m) where
compact (Right (Just x)) = Right x
compact (Right _) = Left mempty
compact (Left x) = Left x
fmapMaybe f (Right x) = maybe (Left mempty) Right (f x)
fmapMaybe _ (Left x) = Left x
separate = \case
Right (Left l) -> (Right l, Left mempty)
Right (Right r) -> (Left mempty, Right r)
Left x -> (Left x, Left x)
instance Compactable [] where
compact = catMaybes
fmapMaybe _ [] = []
fmapMaybe f (h:t) = maybe (fmapMaybe f t) (: fmapMaybe f t) (f h)
filter = Prelude.filter
separate = partitionEithers
fmapEither f = foldl' (deal f) ([],[])
where deal g ~(bs, cs) a = case g a of
Left b -> (b:bs, cs)
Right c -> (bs, c:cs)
traverseMaybe f = go where
go (x:xs) = maybe id (:) <$> f x <*> go xs
go [] = pure []
instance Compactable IO where
compact = altDefaultCompact
instance Compactable STM where
compact = altDefaultCompact
instance Compactable Proxy where
compact _ = Proxy
separate _ = (Proxy, Proxy)
filter _ _ = Proxy
fmapMaybe _ _ = Proxy
applyMaybe _ _ = Proxy
bindMaybe _ _ = Proxy
instance Compactable Option where
compact (Option x) = Option (join x)
fmapMaybe f (Option (Just x)) = Option (f x)
fmapMaybe _ _ = Option Nothing
separate = altDefaultSeparate
instance Compactable ReadP
instance Compactable ReadPrec
instance ( Functor f, Functor g, Compactable f, Compactable g )
=> Compactable (FP.Product f g) where
compact (FP.Pair x y) = FP.Pair (compact x) (compact y)
instance ( Functor f, Functor g, Compactable g )
=> Compactable (Compose f g) where
compact = fmapMaybe id
fmapMaybe f (Compose fg) = Compose $ fmapMaybe f <$> fg
instance Compactable IntMap.IntMap where
compact = IntMap.mapMaybe id
fmapMaybe = IntMap.mapMaybe
filter = IntMap.filter
separate = IntMap.mapEither id
fmapEither = IntMap.mapEither
instance Compactable (Map.Map k) where
compact = Map.mapMaybe id
fmapMaybe = Map.mapMaybe
filter = Map.filter
separate = Map.mapEither id
fmapEither = Map.mapEither
instance Compactable Seq.Seq where
compact = fmap fromJust . Seq.filter isJust
separate = altDefaultSeparate
filter = Seq.filter
instance Compactable V.Vector where
compact = altDefaultCompact
separate = altDefaultSeparate
filter = V.filter
instance Compactable (Const r) where
compact (Const r) = Const r
fmapMaybe _ (Const r) = Const r
bindMaybe (Const r) _ = Const r
filter _ (Const r) = Const r
instance Compactable Set.Set where
compact = Set.fromDistinctAscList . compact . Set.toAscList
separate = bimap Set.fromDistinctAscList Set.fromDistinctAscList . separate . Set.toAscList
filter = Set.filter
fforMaybe :: (Compactable f, Functor f) => f a -> (a -> Maybe b) -> f b
fforMaybe = flip fmapMaybe
fforEither :: (Compactable f, Functor f) => f a -> (a -> Either l r) -> (f l, f r)
fforEither = flip fmapEither
fmapMaybeM :: (Compactable f, Monad f) => (a -> MaybeT f b) -> f a -> f b
fmapMaybeM f = (>>= compact . runMaybeT . f)
fforMaybeM :: (Compactable f, Monad f) => f a -> (a -> MaybeT f b) -> f b
fforMaybeM = flip fmapMaybeM
fmapEitherM :: (Compactable f, Monad f) => (a -> ExceptT l f r) -> f a -> (f l, f r)
fmapEitherM f x = separate $ runExceptT . f =<< x
fforEitherM :: (Compactable f, Monad f) => f a -> (a -> ExceptT l f r) -> (f l, f r)
fforEitherM = flip fmapEitherM
applyMaybeM :: (Compactable f, Monad f) => f (a -> MaybeT f b) -> f a -> f b
applyMaybeM fa = compact . join . fmap runMaybeT . (fa <*>)
bindMaybeM :: (Compactable f, Monad f) => f a -> (a -> f (MaybeT f b)) -> f b
bindMaybeM x = compact . join . fmap runMaybeT . (x >>=)
traverseMaybeM :: (Monad m, Compactable t, Traversable t) => (a -> MaybeT m b) -> t a -> m (t b)
traverseMaybeM f = unwrapMonad . traverseMaybe (WrapMonad . runMaybeT . f)
altDefaultCompact :: (Alternative f, Monad f) => f (Maybe a) -> f a
altDefaultCompact = (>>= maybe empty return)
altDefaultSeparate :: (Alternative f, Foldable f) => f (Either l r) -> (f l, f r)
altDefaultSeparate = foldl' (\(l', r') -> \case
Left l -> (l' <|> pure l ,r')
Right r -> (l', r' <|> pure r)) (empty, empty)
hush :: Either l r -> Maybe r
hush = \case (Right x) -> Just x; _ -> Nothing
flipEither :: Either a b -> Either b a
flipEither = \case (Right x) -> Left x; (Left x) -> Right x