{-# LANGUAGE CPP #-}
{-# LANGUAGE ConstrainedClassMethods #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeOperators #-}
module Control.Compactable
(
Compactable (..)
, CompactFold (..)
, fforMaybe
, fforFold
, fforEither
, fforBifold
, mfold'
, mlefts
, mrights
, fmapMaybeM
, fmapEitherM
, fforMaybeM
, fforEitherM
, applyMaybeM
, bindMaybeM
, traverseMaybeM
, altDefaultCompact
, altDefaultSeparate
) where
import Control.Applicative
import Control.Arrow
import Control.Monad (MonadPlus, join)
import Control.Monad.Trans.Except
import Control.Monad.Trans.Maybe
import Data.Bifoldable
import Data.Bifunctor (bimap)
import Data.Either (partitionEithers)
import Data.Foldable as F (foldl', toList)
import Data.Functor.Compose
import qualified Data.Functor.Product as FP
import qualified Data.IntMap as IntMap
import qualified Data.List as List
import qualified Data.Map as Map
import Data.Maybe
import Data.Monoid
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 GHC.Generics
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 ())
{-# INLINABLE compact #-}
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)
{-# INLINABLE separate #-}
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
{-# INLINABLE filter #-}
partition :: (a -> Bool) -> f a -> (f a, f a)
default partition :: Functor f => (a -> Bool) -> f a -> (f a, f a)
partition f = fmapEither $ \a -> if f a then Right a else Left a
{-# INLINEABLE partition #-}
fmapMaybe :: Functor f => (a -> Maybe b) -> f a -> f b
fmapMaybe f = compact . fmap f
{-# INLINABLE fmapMaybe #-}
fmapEither :: Functor f => (a -> Either l r) -> f a -> (f l, f r)
fmapEither f = separate . fmap f
{-# INLINABLE fmapEither #-}
applyMaybe :: Applicative f => f (a -> Maybe b) -> f a -> f b
applyMaybe fa = compact . (fa <*>)
{-# INLINABLE applyMaybe #-}
applyEither :: Applicative f => f (a -> Either l r) -> f a -> (f l, f r)
applyEither fa = separate . (fa <*>)
{-# INLINABLE applyEither #-}
bindMaybe :: Monad f => f a -> (a -> f (Maybe b)) -> f b
bindMaybe x = compact . (x >>=)
{-# INLINABLE bindMaybe #-}
bindEither :: Monad f => f a -> (a -> f (Either l r)) -> (f l, f r)
bindEither x = separate . (x >>=)
{-# INLINABLE bindEither #-}
traverseMaybe :: (Applicative g, Traversable f)
=> (a -> g (Maybe b)) -> f a -> g (f b)
traverseMaybe f = fmap compact . traverse f
{-# INLINABLE traverseMaybe #-}
traverseEither :: (Applicative g, Traversable f)
=> (a -> g (Either l r)) -> f a -> g (f l, f r)
traverseEither f = fmap separate . traverse f
{-# INLINABLE traverseEither #-}
instance Compactable Maybe where
compact = join
{-# INLINABLE compact #-}
fmapMaybe = (=<<)
{-# INLINABLE fmapMaybe #-}
separate = \case
Just x -> case x of
Left l -> (Just l, Nothing)
Right r -> (Nothing, Just r)
_ -> (Nothing, Nothing)
{-# INLINABLE separate #-}
instance Monoid m => Compactable (Either m) where
compact (Right (Just x)) = Right x
compact (Right _) = Left mempty
compact (Left x) = Left x
{-# INLINABLE compact #-}
fmapMaybe f (Right x) = maybe (Left mempty) Right (f x)
fmapMaybe _ (Left x) = Left x
{-# INLINABLE fmapMaybe #-}
separate = \case
Right (Left l) -> (Right l, Left mempty)
Right (Right r) -> (Left mempty, Right r)
Left x -> (Left x, Left x)
{-# INLINABLE separate #-}
instance Compactable [] where
compact = catMaybes
{-# INLINABLE compact #-}
fmapMaybe _ [] = []
fmapMaybe f (h:t) = maybe (fmapMaybe f t) (: fmapMaybe f t) (f h)
{-# INLINABLE fmapMaybe #-}
filter = Prelude.filter
{-# INLINABLE filter #-}
partition = List.partition
{-# INLINABLE partition #-}
separate = partitionEithers
{-# INLINABLE separate #-}
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)
{-# INLINABLE fmapEither #-}
traverseMaybe f = go where
go (x:xs) = maybe id (:) <$> f x <*> go xs
go [] = pure []
{-# INLINE traverseMaybe #-}
instance Compactable ZipList where
compact (ZipList xs) = ZipList $ compact xs
instance Compactable IO where
compact = altDefaultCompact
{-# INLINABLE compact #-}
instance Compactable STM where
compact = altDefaultCompact
{-# INLINABLE compact #-}
instance Compactable Proxy where
compact _ = Proxy
{-# INLINABLE compact #-}
separate _ = (Proxy, Proxy)
{-# INLINABLE separate #-}
filter _ _ = Proxy
{-# INLINABLE filter #-}
partition _ _ = (Proxy, Proxy)
{-# INLINABLE partition #-}
fmapMaybe _ _ = Proxy
{-# INLINABLE fmapMaybe #-}
applyMaybe _ _ = Proxy
{-# INLINABLE applyMaybe #-}
bindMaybe _ _ = Proxy
{-# INLINABLE bindMaybe #-}
fmapEither _ _ = (Proxy, Proxy)
{-# INLINABLE fmapEither #-}
applyEither _ _ = (Proxy, Proxy)
{-# INLINABLE applyEither #-}
bindEither _ _ = (Proxy, Proxy)
{-# INLINABLE bindEither #-}
instance Compactable U1
instance Compactable Option where
compact (Option x) = Option (join x)
{-# INLINABLE compact #-}
fmapMaybe f (Option (Just x)) = Option (f x)
fmapMaybe _ _ = Option Nothing
{-# INLINABLE fmapMaybe #-}
separate = altDefaultSeparate
{-# INLINABLE separate #-}
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)
{-# INLINABLE compact #-}
instance ( Functor f, Functor g, Compactable g )
=> Compactable (Compose f g) where
compact = fmapMaybe id
{-# INLINABLE compact #-}
fmapMaybe f (Compose fg) = Compose $ fmapMaybe f <$> fg
{-# INLINABLE fmapMaybe #-}
instance Compactable IntMap.IntMap where
compact = IntMap.mapMaybe id
{-# INLINABLE compact #-}
fmapMaybe = IntMap.mapMaybe
{-# INLINABLE fmapMaybe #-}
filter = IntMap.filter
{-# INLINABLE filter #-}
partition = IntMap.partition
{-# INLINABLE partition #-}
separate = IntMap.mapEither id
{-# INLINABLE separate #-}
fmapEither = IntMap.mapEither
{-# INLINABLE fmapEither #-}
instance Compactable (Map.Map k) where
compact = Map.mapMaybe id
{-# INLINABLE compact #-}
fmapMaybe = Map.mapMaybe
{-# INLINABLE fmapMaybe #-}
filter = Map.filter
{-# INLINABLE filter #-}
partition = Map.partition
{-# INLINABLE partition #-}
separate = Map.mapEither id
{-# INLINABLE separate #-}
fmapEither = Map.mapEither
{-# INLINABLE fmapEither #-}
instance Compactable Seq.Seq where
compact = fmap fromJust . Seq.filter isJust
{-# INLINABLE compact #-}
separate = altDefaultSeparate
{-# INLINABLE separate #-}
filter = Seq.filter
{-# INLINABLE filter #-}
partition = Seq.partition
{-# INLINABLE partition #-}
instance Compactable V.Vector where
compact = altDefaultCompact
{-# INLINABLE compact #-}
separate = altDefaultSeparate
{-# INLINABLE separate #-}
filter = V.filter
{-# INLINABLE filter #-}
partition = V.partition
{-# INLINABLE partition #-}
instance Compactable (Const r) where
compact (Const r) = Const r
{-# INLINABLE compact #-}
fmapMaybe _ (Const r) = Const r
{-# INLINABLE fmapMaybe #-}
applyMaybe _ (Const r) = Const r
{-# INLINABLE applyMaybe #-}
bindMaybe (Const r) _ = Const r
{-# INLINABLE bindMaybe #-}
fmapEither _ (Const r) = (Const r, Const r)
{-# INLINABLE fmapEither #-}
applyEither _ (Const r) = (Const r, Const r)
{-# INLINABLE applyEither #-}
bindEither (Const r) _ = (Const r, Const r)
{-# INLINABLE bindEither #-}
filter _ (Const r) = Const r
{-# INLINABLE filter #-}
partition _ (Const r) = (Const r, Const r)
{-# INLINABLE partition #-}
instance Compactable Set.Set where
compact = Set.fromDistinctAscList . compact . Set.toAscList
{-# INLINABLE compact #-}
separate = bimap Set.fromDistinctAscList Set.fromDistinctAscList . separate . Set.toAscList
{-# INLINABLE separate #-}
filter = Set.filter
{-# INLINABLE filter #-}
partition = Set.partition
{-# INLINABLE partition #-}
instance (ArrowPlus a, ArrowApply a) => Compactable (ArrowMonad a) where
instance Monad a => Compactable (WrappedMonad a) where
instance Functor a => Compactable (Rec1 a) where
instance Functor a => Compactable (Alt a) where
instance (Functor a, Functor b) => Compactable (a :*: b)
instance Functor f => Compactable (M1 i c f)
instance (Functor f, Functor g) => Compactable (f :.: g)
newtype AltSum f a = AltSum { unAltSum :: f a }
deriving (Functor, Applicative, Alternative)
#if __GLASGOW_HASKELL__ > 840
instance Alternative f => Monoid (AltSum f a) where
mempty = empty
AltSum a `mappend` AltSum b = AltSum (a <|> b)
#else
instance Alternative f => Semigroup (AltSum f a) where
AltSum a <> AltSum b = AltSum (a <|> b)
instance Alternative f => Monoid (AltSum f a) where
mappend = (Data.Semigroup.<>)
mempty = empty
#endif
class Compactable f => CompactFold (f :: * -> *) where
compactFold :: Foldable g => f (g a) -> f a
default compactFold :: (Monad f, Alternative f, Foldable g) => f (g a) -> f a
compactFold = (>>= mfold')
{-# INLINEABLE compactFold #-}
separateFold :: Bifoldable g => f (g a b) -> (f a, f b)
default separateFold :: (Monad f, Alternative f, Bifoldable g) => f (g a b) -> (f a, f b)
separateFold xs = (xs >>= mlefts, xs >>= mrights)
{-# INLINEABLE separateFold #-}
fmapFold :: (Functor f, Foldable g) => (a -> g b) -> f a -> f b
fmapFold f = compactFold . fmap f
{-# INLINABLE fmapFold #-}
fmapBifold :: (Functor f, Bifoldable g) => (a -> g l r) -> f a -> (f l, f r)
fmapBifold f = separateFold . fmap f
{-# INLINABLE fmapBifold #-}
applyFold :: (Applicative f, Foldable g) => f (a -> g b) -> f a -> f b
applyFold f = compactFold . (f <*>)
{-# INLINABLE applyFold #-}
applyBifold :: (Applicative f, Bifoldable g) => f (a -> g l r) -> f a -> (f l, f r)
applyBifold fa = separateFold . (fa <*>)
{-# INLINABLE applyBifold #-}
bindFold :: (Monad f, Foldable g) => f a -> (a -> f (g b)) -> f b
bindFold f = compactFold . (f >>=)
{-# INLINABLE bindFold #-}
bindBifold :: (Monad f, Bifoldable g) => f a -> (a -> f (g l r)) -> (f l, f r)
bindBifold f = separateFold . (f >>=)
{-# INLINABLE bindBifold #-}
traverseFold :: (Applicative h, Foldable g, Traversable f) => (a -> h (g b)) -> f a -> h (f b)
traverseFold f = fmap compactFold . traverse f
{-# INLINABLE traverseFold #-}
traverseBifold :: (Applicative h, Bifoldable g, Traversable f) => (a -> h (g l r)) -> f a -> h (f l, f r)
traverseBifold f = fmap separateFold . traverse f
{-# INLINABLE traverseBifold #-}
mfold' :: (Foldable f, Alternative m) => f a -> m a
mfold' = unAltSum . foldMap (AltSum . pure)
mlefts :: (Bifoldable f, Alternative m) => f a b -> m a
mlefts = unAltSum . bifoldMap (AltSum . pure) (const mempty)
mrights :: (Bifoldable f, Alternative m) => f a b -> m b
mrights = unAltSum . bifoldMap (const mempty) (AltSum . pure)
instance CompactFold [] where
compactFold = (>>= F.toList)
{-# INLINEABLE compactFold #-}
instance CompactFold Maybe
instance CompactFold IO
instance CompactFold ReadP
instance CompactFold ReadPrec
instance CompactFold STM
instance CompactFold ZipList where
compactFold (ZipList xs) = ZipList $ compactFold xs
separateFold (ZipList xs) = bimap ZipList ZipList $ separateFold xs
instance CompactFold Option
instance CompactFold U1
instance CompactFold Proxy
instance (ArrowPlus a, ArrowApply a) => CompactFold (ArrowMonad a)
instance MonadPlus a => CompactFold (WrappedMonad a)
instance (Alternative a, Monad a) => CompactFold (Rec1 a)
instance (Alternative a, Monad a) => CompactFold (Alt a)
instance (Alternative f, Monad f, Alternative g, Monad g) => CompactFold (f :*: g)
instance (Compactable f, Alternative f, Monad f, Compactable g, Alternative g, Monad g) => CompactFold (FP.Product f g)
instance (Alternative f, Monad f) => CompactFold (M1 i c f)
fforMaybe :: (Compactable f, Functor f) => f a -> (a -> Maybe b) -> f b
fforMaybe = flip fmapMaybe
fforFold :: (CompactFold f, Functor f, Foldable g) => f a -> (a -> g b) -> f b
fforFold = flip fmapFold
fforEither :: (Compactable f, Functor f) => f a -> (a -> Either l r) -> (f l, f r)
fforEither = flip fmapEither
fforBifold :: (CompactFold f, Functor f, Bifoldable g) => f a -> (a -> g l r) -> (f l, f r)
fforBifold = flip fmapBifold
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)
{-# INLINABLE altDefaultCompact #-}
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)
{-# INLINABLE altDefaultSeparate #-}
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