witherable 0.2 → 0.3
raw patch · 2 files changed
+76/−24 lines, 2 filesdep +transformers-compatdep ~containers
Dependencies added: transformers-compat
Dependency ranges changed: containers
Files
- src/Data/Witherable.hs +72/−21
- witherable.cabal +4/−3
src/Data/Witherable.hs view
@@ -17,8 +17,6 @@ module Data.Witherable ( Filterable(..) , Witherable(..)- , witherM- , blightM , ordNub , hashNub , forMaybe@@ -43,13 +41,16 @@ import qualified Data.Map.Lazy as M import qualified Data.Sequence as S import qualified Data.Vector as V-import qualified Data.HashMap.Strict as HM+import qualified Data.HashMap.Lazy as HM import qualified Data.Set as Set import qualified Data.HashSet as HSet import Control.Applicative import qualified Data.Traversable as T import qualified Data.Foldable as F import Data.Functor.Compose+import Data.Functor.Product as P+import Data.Functor.Sum as Sum+import Control.Monad.Trans.Identity import Data.Hashable import Data.Functor.Identity import Control.Monad.Trans.Maybe@@ -59,6 +60,9 @@ #if (MIN_VERSION_base(4,7,0)) import Data.Proxy #endif+#if __GLASGOW_HASKELL__ >= 708+import Data.Coerce (coerce)+#endif import Prelude -- Fix redundant import warning -- | This type allows combinators to take a 'Filter' specializing the parameter @f@.@@ -87,6 +91,10 @@ {-# INLINE pure #-} Peat f <*> Peat g = Peat $ \h -> f h <*> g h {-# INLINE (<*>) #-}+#if MIN_VERSION_base(4,10,0)+ liftA2 f (Peat xs) (Peat ys) = Peat $ \h -> liftA2 f (xs h) (ys h)+ {-# INLINE liftA2 #-}+#endif -- | Reconstitute a 'Filter' from its monomorphic form. cloneFilter :: FilterLike (Peat a b) s t a b -> Filter s t a b@@ -103,9 +111,19 @@ forMaybeOf = flip {-# INLINE forMaybeOf #-} +-- In case mapMaybeOf or filterOf is called with a function of+-- unknown arity, we don't want to slow things down to raise+-- its arity.+idDot :: (a -> b) -> a -> Identity b+#if __GLASGOW_HASKELL__ >= 708+idDot = coerce+#else+idDot = (Identity .)+#endif+ -- | 'mapMaybe' through a filter. mapMaybeOf :: FilterLike Identity s t a b -> (a -> Maybe b) -> s -> t-mapMaybeOf w f = runIdentity . w (Identity . f)+mapMaybeOf w f = runIdentity . w (idDot f) {-# INLINE mapMaybeOf #-} -- | 'catMaybes' through a filter.@@ -120,7 +138,7 @@ -- | Filter each element of a structure targeted by a 'Filter'. filterOf :: FilterLike' Identity s a -> (a -> Bool) -> s -> s-filterOf w f = runIdentity . filterAOf w (Identity . f)+filterOf w f = runIdentity . filterAOf w (idDot f) {-# INLINE filterOf #-} -- | Like 'Functor', but it include 'Maybe' effects.@@ -132,6 +150,9 @@ -- [/identity/] -- @'mapMaybe' Just ≡ id@ --+-- [/conservation/]+-- @'mapMaybe' (Just . f) ≡ 'fmap' f@+-- -- [/composition/] -- @'mapMaybe' f . 'mapMaybe' g ≡ 'mapMaybe' (f <=< g)@ class Functor f => Filterable f where@@ -161,6 +182,9 @@ -- [/identity/] -- @'wither' ('pure' . Just) ≡ 'pure'@ --+-- [/conservation/]+-- @'wither' ('fmap' 'Just' . f) ≡ 'traverse' f@+-- -- [/composition/] -- @'Compose' . 'fmap' ('wither' f) . 'wither' g ≡ 'wither' ('Compose' . 'fmap' ('wither' f) . g)@ --@@ -176,6 +200,17 @@ wither f = fmap catMaybes . T.traverse f {-# INLINE wither #-} + -- | @Monadic variant of 'wither'. This may have more efficient implementation.@+ witherM :: Monad m => (a -> m (Maybe b)) -> t a -> m (t b)+#if MIN_VERSION_base(4,8,0)+ witherM = wither+#elif __GLASGOW_HASKELL__ >= 708+ witherM f = unwrapMonad . wither (coerce f)+#else+ witherM f = unwrapMonad . wither (WrapMonad . f)+#endif+ {-# INLINE witherM #-}+ -- | @'Compose' . 'fmap' ('filterA' f) . 'filterA' g ≡ 'filterA' (\x -> 'Compose' $ 'fmap' (\b -> (b&&) <$> f x) (g x)@ filterA :: Applicative f => (a -> f Bool) -> t a -> f (t a) filterA = filterAOf wither@@ -189,16 +224,6 @@ forMaybe = flip wither {-# INLINE forMaybe #-} --- | A variant of `wither` that works on 'MaybeT'.-witherM :: (Witherable t, Monad m) => (a -> MaybeT m b) -> t a -> m (t b)-witherM f = unwrapMonad . wither (WrapMonad . runMaybeT . f)-{-# INLINE witherM #-}---- | 'blightM' is 'witherM' with its arguments flipped.-blightM :: (Monad m, Witherable t) => t a -> (a -> MaybeT m b) -> m (t b)-blightM = flip witherM-{-# INLINE blightM #-}- -- | Remove the duplicate elements through a filter. ordNubOf :: Ord a => FilterLike' (State (Set.Set a)) s a -> s -> s ordNubOf w t = evalState (w f t) Set.empty@@ -222,14 +247,14 @@ -- occurrence. This is asymptotically faster than using -- 'Data.List.nub' from "Data.List". ordNub :: (Witherable t, Ord a) => t a -> t a-ordNub = ordNubOf wither+ordNub = ordNubOf witherM {-# INLINE ordNub #-} -- | Removes duplicate elements from a list, keeping only the first -- occurrence. This is usually faster than 'ordNub', especially for--- things that have a slow comparion (like 'String').+-- things that have a slow comparison (like 'String'). hashNub :: (Witherable t, Eq a, Hashable a) => t a -> t a-hashNub = hashNubOf wither+hashNub = hashNubOf witherM {-# INLINE hashNub #-} instance Filterable Maybe where@@ -258,7 +283,7 @@ instance Witherable [] where wither f = go where- go (x:xs) = maybe id (:) <$> f x <*> go xs+ go (x:xs) = liftA2 (maybe id (:)) (f x) (go xs) go [] = pure [] {-# INLINE[0] wither #-} @@ -273,6 +298,9 @@ filter = M.filter instance Witherable (M.Map k) where+#if MIN_VERSION_containers(0,5,8)+ wither f = M.traverseMaybeWithKey (const f)+#endif instance (Eq k, Hashable k) => Filterable (HM.HashMap k) where mapMaybe = HM.mapMaybe@@ -297,8 +325,7 @@ {-# INLINABLE wither #-} instance Filterable V.Vector where- mapMaybe f = V.fromList . mapMaybe f . V.toList- {-# INLINABLE mapMaybe #-}+ mapMaybe = V.mapMaybe instance Witherable V.Vector where wither f = fmap V.fromList . wither f . V.toList@@ -312,11 +339,35 @@ wither f = fmap S.fromList . wither f . F.toList {-# INLINABLE wither #-} +-- The instances for Compose, Product, and Sum are not entirely+-- unique. Any particular composition, product, or sum of functors+-- may support a variety of 'wither' implementations.+ instance (Functor f, Filterable g) => Filterable (Compose f g) where mapMaybe f = Compose . fmap (mapMaybe f) . getCompose instance (T.Traversable f, Witherable g) => Witherable (Compose f g) where wither f = fmap Compose . T.traverse (wither f) . getCompose++instance (Filterable f, Filterable g) => Filterable (P.Product f g) where+ mapMaybe f (P.Pair x y) = P.Pair (mapMaybe f x) (mapMaybe f y)++instance (Witherable f, Witherable g) => Witherable (P.Product f g) where+ wither f (P.Pair x y) = liftA2 P.Pair (wither f x) (wither f y)++instance (Filterable f, Filterable g) => Filterable (Sum.Sum f g) where+ mapMaybe f (Sum.InL x) = Sum.InL (mapMaybe f x)+ mapMaybe f (Sum.InR y) = Sum.InR (mapMaybe f y)++instance (Witherable f, Witherable g) => Witherable (Sum.Sum f g) where+ wither f (Sum.InL x) = Sum.InL <$> wither f x+ wither f (Sum.InR y) = Sum.InR <$> wither f y++instance Filterable f => Filterable (IdentityT f) where+ mapMaybe f (IdentityT m) = IdentityT (mapMaybe f m)++instance Witherable f => Witherable (IdentityT f) where+ wither f (IdentityT m) = IdentityT <$> wither f m instance Functor f => Filterable (MaybeT f) where mapMaybe f = MaybeT . fmap (mapMaybe f) . runMaybeT
witherable.cabal view
@@ -1,5 +1,5 @@ name: witherable-version: 0.2+version: 0.3 synopsis: filterable traversable description: A stronger variant of `traverse` which can remove elements and generalised mapMaybe, catMaybes, filter homepage: https://github.com/fumieval/witherable@@ -12,7 +12,7 @@ build-type: Simple -- extra-source-files: cabal-version: >=1.10-tested-With: GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.2+tested-With: GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.3 source-repository head type: git@@ -24,9 +24,10 @@ -- other-extensions: build-depends: base == 4.*, base-orphans,- containers,+ containers >= 0.5, hashable, transformers,+ transformers-compat, unordered-containers, vector hs-source-dirs: src