witherable 0.4.2 → 0.5
raw patch · 4 files changed
+45/−174 lines, 4 filesdep ~containersdep ~hashable
Dependency ranges changed: containers, hashable
Files
- CHANGELOG.md +8/−0
- src/Data/Witherable.hs +0/−163
- src/Witherable.hs +29/−3
- witherable.cabal +8/−8
CHANGELOG.md view
@@ -1,3 +1,11 @@+0.5+---++* `FilterableWithIndex` is a superclass of `WitherableWithIndex`.+* Remove deprecated `Data.Witherable` module. Use `Witherable` module.+* Relax `FilterableWithIndex` composition law so list-like instances are lawful.+* Add `drain :: f a -> f b` method (with default implementation `drain = mapMaybe (Const Nothing)`) to the `Filterable`.+ 0.4.2 -------
− src/Data/Witherable.hs
@@ -1,163 +0,0 @@-{-# LANGUAGE Rank2Types #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE Trustworthy #-}--------------------------------------------------------------------------------- |--- Module : Data.Witherable--- Copyright : (c) Fumiaki Kinoshita 2015--- License : BSD3------ Maintainer : Fumiaki Kinoshita <fumiexcel@gmail.com>--- Stability : provisional--- Portability : non-portable----------------------------------------------------------------------------------module Data.Witherable {-# DEPRECATED "Use Witherable instead" #-}- ( Filterable(..)- , (<$?>)- , (<&?>)- , Witherable(..)- , ordNub- , ordNubOn- , hashNub- , hashNubOn- , forMaybe- -- * Indexed variants- , FilterableWithIndex(..)- , WitherableWithIndex(..)- -- * Generalization- , WitherLike, Wither, WitherLike', Wither'- , FilterLike, Filter, FilterLike', Filter'- , witherOf- , forMaybeOf- , mapMaybeOf- , catMaybesOf- , filterAOf- , filterOf- , ordNubOf- , ordNubOnOf- , hashNubOf- , hashNubOnOf- -- * Cloning- , cloneFilter- , Peat(..)- -- * Wrapper- , WrappedFoldable(..)- ) where--import Control.Applicative-import Data.Functor.Identity-import Witherable-import qualified Data.Set as Set-import qualified Data.HashSet as HSet-import Control.Monad.Trans.State.Strict-import Data.Hashable-import Data.Coerce--type Filter s t a b = Wither s t a b-{-# DEPRECATED Filter "Use Wither instead" #-}-type FilterLike f s t a b = WitherLike f s t a b-{-# DEPRECATED FilterLike "Use WitherLike instead" #-}-type Filter' s a = Wither' s a-{-# DEPRECATED Filter' "Use Filter' instead" #-}-type FilterLike' f s a = WitherLike' f s a-{-# DEPRECATED FilterLike' "Use WitherLike' instead" #-}---- | This type allows combinators to take a 'Filter' specializing the parameter @f@.-type WitherLike f s t a b = (a -> f (Maybe b)) -> s -> f t---- | A 'Wither' is like a <http://hackage.haskell.org/package/lens-4.13.2.1/docs/Control-Lens-Type.html#t:Traversal Traversal>,--- but you can also remove targets.-type Wither s t a b = forall f. Applicative f => WitherLike f s t a b---- | A simple 'WitherLike'.-type WitherLike' f s a = WitherLike f s s a a---- | A simple 'Wither'.-type Wither' s a = forall f. Applicative f => WitherLike' f s a---- | This is used to characterize and clone a 'Filter'.--- Since @FilterLike (Peat a b) s t a b@ is monomorphic, it can be used to store a filter in a container.-newtype Peat a b t = Peat { runPeat :: forall f. Applicative f => (a -> f (Maybe b)) -> f t }--instance Functor (Peat a b) where- fmap f (Peat k) = Peat (fmap f . k)- {-# INLINE fmap #-}--instance Applicative (Peat a b) where- pure a = Peat $ const (pure a)- {-# 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-cloneFilter l f = (\a -> a `runPeat` f) . l (\a -> Peat $ \g -> g a)-{-# INLINABLE cloneFilter #-}---- | 'witherOf' is actually 'id', but left for consistency.-witherOf :: FilterLike f s t a b -> (a -> f (Maybe b)) -> s -> f t-witherOf = id-{-# INLINE witherOf #-}---- | @'forMaybeOf' ≡ 'flip'@-forMaybeOf :: FilterLike f s t a b -> s -> (a -> f (Maybe b)) -> f t-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-idDot = coerce---- | 'mapMaybe' through a filter.-mapMaybeOf :: FilterLike Identity s t a b -> (a -> Maybe b) -> s -> t-mapMaybeOf w f = runIdentity . w (idDot f)-{-# INLINE mapMaybeOf #-}---- | 'catMaybes' through a filter.-catMaybesOf :: FilterLike Identity s t (Maybe a) a -> s -> t-catMaybesOf w = mapMaybeOf w id-{-# INLINE catMaybesOf #-}---- | 'filterA' through a filter.-filterAOf :: Functor f => FilterLike' f s a -> (a -> f Bool) -> s -> f s-filterAOf w f = w $ \a -> (\b -> if b then Just a else Nothing) <$> f a-{-# INLINABLE filterAOf #-}---- | 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 (idDot f)-{-# INLINE filterOf #-}---- | Remove the duplicate elements through a filter.-ordNubOf :: Ord a => FilterLike' (State (Set.Set a)) s a -> s -> s-ordNubOf w = ordNubOnOf w id---- | Remove the duplicate elements through a filter.-ordNubOnOf :: Ord b => FilterLike' (State (Set.Set b)) s a -> (a -> b) -> s -> s-ordNubOnOf w p t = evalState (w f t) Set.empty- where- f a = let b = p a in state $ \s -> if Set.member b s- then (Nothing, s)- else (Just a, Set.insert b s)-{-# INLINE ordNubOf #-}---- | Remove the duplicate elements through a filter.--- It is often faster than 'ordNubOf', especially when the comparison is expensive.-hashNubOf :: (Eq a, Hashable a) => FilterLike' (State (HSet.HashSet a)) s a -> s -> s-hashNubOf w = hashNubOnOf w id---- | Remove the duplicate elements through a filter.-hashNubOnOf :: (Eq b, Hashable b) => FilterLike' (State (HSet.HashSet b)) s a -> (a -> b) -> s -> s-hashNubOnOf w p t = evalState (w f t) HSet.empty- where- f a = let b = p a in state $ \s -> if HSet.member b s- then (Nothing, s)- else (Just a, HSet.insert b s)-{-# INLINE hashNubOf #-}
src/Witherable.hs view
@@ -84,6 +84,7 @@ -- -- [/composition/] -- @'mapMaybe' f . 'mapMaybe' g ≡ 'mapMaybe' (f <=< g)@+-- class Functor f => Filterable f where -- | Like 'Maybe.mapMaybe'. mapMaybe :: (a -> Maybe b) -> f a -> f b@@ -100,6 +101,14 @@ filter f = mapMaybe $ \a -> if f a then Just a else Nothing {-# INLINE filter #-} + -- | Empty a filterable.+ --+ -- @'drain' ≡ 'mapMaybe' (const Nothing)@+ --+ drain :: f a -> f b+ drain = mapMaybe (const Nothing)+ {-# INLINE drain #-}+ {-# MINIMAL mapMaybe | catMaybes #-} -- | An enhancement of 'Traversable' with 'Filterable'@@ -169,6 +178,7 @@ instance Filterable Maybe where mapMaybe f = (>>= f)+ drain _ = Nothing {-# INLINE mapMaybe #-} instance Witherable Maybe where@@ -180,6 +190,7 @@ instance Filterable Option where mapMaybe f = (>>= Option . f)+ drain _ = Option Nothing {-# INLINE mapMaybe #-} instance Witherable Option where@@ -197,6 +208,9 @@ mapMaybe f (Right a) = maybe (Left mempty) Right $ f a {-# INLINABLE mapMaybe #-} + drain (Left e) = Left e+ drain (Right _) = Left mempty+ instance Monoid e => Witherable (Either e) where wither _ (Left e) = pure (Left e) wither f (Right a) = fmap (maybe (Left mempty) Right) (f a)@@ -206,11 +220,13 @@ mapMaybe = Maybe.mapMaybe catMaybes = Maybe.catMaybes filter = Prelude.filter+ drain _ = [] instance Filterable ZipList where mapMaybe f = ZipList . Maybe.mapMaybe f . getZipList catMaybes = ZipList . Maybe.catMaybes . getZipList filter f = ZipList . Prelude.filter f . getZipList+ drain _ = ZipList [] -- | Methods are good consumers for fusion. instance Witherable [] where@@ -236,12 +252,14 @@ instance Filterable IM.IntMap where mapMaybe = IM.mapMaybe filter = IM.filter+ drain _ = IM.empty instance Witherable IM.IntMap where instance Filterable (M.Map k) where mapMaybe = M.mapMaybe filter = M.filter+ drain _ = M.empty instance Witherable (M.Map k) where #if MIN_VERSION_containers(0,5,8)@@ -251,6 +269,7 @@ instance (Eq k, Hashable k) => Filterable (HM.HashMap k) where mapMaybe = HM.mapMaybe filter = HM.filter+ drain _ = HM.empty instance (Eq k, Hashable k) => Witherable (HM.HashMap k) where @@ -271,6 +290,7 @@ instance Filterable V.Vector where filter = V.filter mapMaybe = V.mapMaybe+ drain _ = V.empty instance Witherable V.Vector where wither f = fmap V.fromList . wither f . V.toList@@ -283,6 +303,7 @@ mapMaybe f = S.fromList . mapMaybe f . F.toList {-# INLINABLE mapMaybe #-} filter = S.filter+ drain _ = S.empty instance Witherable S.Seq where wither f = fmap S.fromList . wither f . F.toList@@ -313,6 +334,7 @@ mapMaybe f = Compose . fmap (mapMaybe f) . getCompose filter p = Compose . fmap (filter p) . getCompose catMaybes = Compose . fmap catMaybes . getCompose+ drain = Compose . fmap drain . getCompose instance (T.Traversable f, Witherable g) => Witherable (Compose f g) where wither f = fmap Compose . T.traverse (wither f) . getCompose@@ -323,6 +345,7 @@ mapMaybe f (P.Pair x y) = P.Pair (mapMaybe f x) (mapMaybe f y) filter p (P.Pair x y) = P.Pair (filter p x) (filter p y) catMaybes (P.Pair x y) = P.Pair (catMaybes x) (catMaybes y)+ drain (P.Pair x y) = P.Pair (drain x) (drain 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)@@ -339,6 +362,9 @@ filter p (Sum.InL x) = Sum.InL (filter p x) filter p (Sum.InR y) = Sum.InR (filter p y) + drain (Sum.InL x) = Sum.InL (drain x)+ drain (Sum.InR y) = Sum.InR (drain 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@@ -470,13 +496,13 @@ imapMaybe f = catMaybes . imap f {-# INLINE imapMaybe #-} - -- | @'ifilter' f . 'ifilter' g ≡ ifilter (\i -> 'liftA2' ('&&') (f i) (g i))@+ -- | @'filter' f . 'ifilter' g ≡ ifilter (\i x -> f x '&&' g i x)@ ifilter :: (i -> a -> Bool) -> t a -> t a ifilter f = imapMaybe $ \i a -> if f i a then Just a else Nothing {-# INLINE ifilter #-} -- | Indexed variant of 'Witherable'.-class (TraversableWithIndex i t, Witherable t) => WitherableWithIndex i t | t -> i where+class (TraversableWithIndex i t, FilterableWithIndex i t, Witherable t) => WitherableWithIndex i t | t -> i where -- | Effectful 'imapMaybe'. -- -- @'iwither' (\ i -> 'pure' . f i) ≡ 'pure' . 'imapMaybe' f@@@ -669,7 +695,7 @@ hashNub = hashNubOn id {-# INLINE hashNub #-} --- | The 'hashNubOn' function behaves just like 'ordNub',+-- | The 'hashNubOn' function behaves just like 'hashNub', -- except it uses a another type to determine equivalence classes. -- -- >>> hashNubOn fst [(True, 'x'), (False, 'y'), (True, 'z')]
witherable.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name: witherable-version: 0.4.2+version: 0.5 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,8 @@ category: Data build-type: Simple extra-source-files: CHANGELOG.md-tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.4 || ==9.0.1+tested-with: GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.7 ||+ ==9.0.1 || ==9.2.8 || ==9.4.8 || ==9.6.5 || ==9.8.2 || ==9.10.1 source-repository head type: git@@ -22,14 +23,13 @@ library exposed-modules: Witherable- Data.Witherable build-depends: base >=4.9 && <5,- base-orphans >=0.8.4 && <0.9,- containers >=0.5.7.1 && <0.7,- hashable >=1.2.7.0 && <1.4,- transformers >=0.5.2.0 && <0.6,+ base-orphans >=0.8.4 && <0.10,+ containers >=0.5.7.1 && <0.8,+ hashable >=1.2.7.0 && <1.5,+ transformers >=0.5.2.0 && <0.7, unordered-containers >=0.2.12.0 && <0.3,- vector >=0.12.2.0 && <0.13,+ vector >=0.12.2.0 && <0.14, indexed-traversable >=0.1.1 && <0.2, indexed-traversable-instances >=0.1 && <0.2 hs-source-dirs: src