mono-traversable 0.9.2.1 → 0.9.3
raw patch · 5 files changed
+175/−6 lines, 5 filesdep +splitPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: split
API changes (from Hackage documentation)
+ Data.Sequences: defaultIntercalate :: IsSequence seq => seq -> [seq] -> seq
+ Data.Sequences: defaultSplitOn :: EqSequence s => s -> s -> [s]
+ Data.Sequences: defaultSplitWhen :: IsSequence seq => (Element seq -> Bool) -> seq -> [seq]
+ Data.Sequences: intercalate :: IsSequence seq => seq -> [seq] -> seq
+ Data.Sequences: splitElem :: EqSequence seq => Element seq -> seq -> [seq]
+ Data.Sequences: splitSeq :: EqSequence seq => seq -> seq -> [seq]
+ Data.Sequences: splitWhen :: IsSequence seq => (Element seq -> Bool) -> seq -> [seq]
- Data.Sequences: class (MonoFoldableEq seq, IsSequence seq, Eq (Element seq)) => EqSequence seq where stripPrefix x y = fmap fromList (otoList x `stripPrefix` otoList y) stripSuffix x y = fmap fromList (otoList x `stripSuffix` otoList y) isPrefixOf x y = otoList x `isPrefixOf` otoList y isSuffixOf x y = otoList x `isSuffixOf` otoList y isInfixOf x y = otoList x `isInfixOf` otoList y group = groupBy (==) groupAll = groupAllOn id
+ Data.Sequences: class (MonoFoldableEq seq, IsSequence seq, Eq (Element seq)) => EqSequence seq where splitElem x = splitWhen (== x) splitSeq = defaultSplitOn stripPrefix x y = fmap fromList (otoList x `stripPrefix` otoList y) stripSuffix x y = fmap fromList (otoList x `stripSuffix` otoList y) isPrefixOf x y = otoList x `isPrefixOf` otoList y isSuffixOf x y = otoList x `isSuffixOf` otoList y isInfixOf x y = otoList x `isInfixOf` otoList y group = groupBy (==) groupAll = groupAllOn id
- Data.Sequences: class (Monoid seq, MonoTraversable seq, SemiSequence seq, MonoPointed seq) => IsSequence seq where fromList = mconcat . fmap singleton break f = (fromList *** fromList) . break f . otoList span f = (fromList *** fromList) . span f . otoList dropWhile f = fromList . dropWhile f . otoList takeWhile f = fromList . takeWhile f . otoList splitAt i = (fromList *** fromList) . genericSplitAt i . otoList unsafeSplitAt i seq = (unsafeTake i seq, unsafeDrop i seq) take i = fst . splitAt i unsafeTake = take drop i = snd . splitAt i unsafeDrop = drop partition f = (fromList *** fromList) . partition f . otoList uncons = fmap (second fromList) . uncons . otoList unsnoc = fmap (first fromList) . unsnoc . otoList filter f = fromList . filter f . otoList filterM f = liftM fromList . filterM f . otoList replicate i = fromList . genericReplicate i replicateM i = liftM fromList . replicateM (fromIntegral i) groupBy f = fmap fromList . groupBy f . otoList groupAllOn f = fmap fromList . groupAllOn f . otoList subsequences = map fromList . subsequences . otoList permutations = map fromList . permutations . otoList tailEx = snd . maybe (error "Data.Sequences.tailEx") id . uncons initEx = fst . maybe (error "Data.Sequences.initEx") id . unsnoc unsafeTail = tailEx unsafeInit = initEx index seq' idx = headMay (drop idx seq') indexEx seq' idx = maybe (error "Data.Sequences.indexEx") id (index seq' idx) unsafeIndex = indexEx
+ Data.Sequences: class (Monoid seq, MonoTraversable seq, SemiSequence seq, MonoPointed seq) => IsSequence seq where fromList = mconcat . fmap singleton break f = (fromList *** fromList) . break f . otoList span f = (fromList *** fromList) . span f . otoList dropWhile f = fromList . dropWhile f . otoList takeWhile f = fromList . takeWhile f . otoList splitAt i = (fromList *** fromList) . genericSplitAt i . otoList unsafeSplitAt i seq = (unsafeTake i seq, unsafeDrop i seq) take i = fst . splitAt i unsafeTake = take drop i = snd . splitAt i unsafeDrop = drop partition f = (fromList *** fromList) . partition f . otoList uncons = fmap (second fromList) . uncons . otoList unsnoc = fmap (first fromList) . unsnoc . otoList filter f = fromList . filter f . otoList filterM f = liftM fromList . filterM f . otoList replicate i = fromList . genericReplicate i replicateM i = liftM fromList . replicateM (fromIntegral i) groupBy f = fmap fromList . groupBy f . otoList groupAllOn f = fmap fromList . groupAllOn f . otoList subsequences = map fromList . subsequences . otoList permutations = map fromList . permutations . otoList tailEx = snd . maybe (error "Data.Sequences.tailEx") id . uncons initEx = fst . maybe (error "Data.Sequences.initEx") id . unsnoc unsafeTail = tailEx unsafeInit = initEx index seq' idx = headMay (drop idx seq') indexEx seq' idx = maybe (error "Data.Sequences.indexEx") id (index seq' idx) unsafeIndex = indexEx intercalate = defaultIntercalate splitWhen = defaultSplitWhen
Files
- ChangeLog.md +4/−0
- mono-traversable.cabal +2/−1
- src/Data/MonoTraversable.hs +0/−1
- src/Data/Sequences.hs +109/−3
- test/Spec.hs +60/−1
ChangeLog.md view
@@ -1,3 +1,7 @@+## 0.9.3++* Added `intercalate`, `splitWhen`, `splitElem`, and `splitSeq` [#80](https://github.com/snoyberg/mono-traversable/pull/80)+ ## 0.9.2.1 * Tweak test suite for 32-bit systems [#78](https://github.com/snoyberg/mono-traversable/issues/78)
mono-traversable.cabal view
@@ -1,5 +1,5 @@ name: mono-traversable-version: 0.9.2.1+version: 0.9.3 synopsis: Type classes for mapping, folding, and traversing monomorphic containers description: Monomorphic variants of the Functor, Foldable, and Traversable typeclasses. If you understand Haskell's basic typeclasses, you understand mono-traversable. In addition to what you are used to, it adds on an IsSequence typeclass and has code for marking data structures as non-empty. homepage: https://github.com/snoyberg/mono-traversable@@ -37,6 +37,7 @@ , vector-algorithms >= 0.6 , dlist >= 0.6 && < 1.0 , dlist-instances == 0.1.*+ , split >= 0.2 hs-source-dirs: src default-language: Haskell2010
src/Data/MonoTraversable.hs view
@@ -599,7 +599,6 @@ unsafeLast = V.unsafeLast maximumByEx = V.maximumBy minimumByEx = V.minimumBy- {-# INLINE ofoldMap #-} {-# INLINE ofoldr #-} {-# INLINE ofoldl' #-} {-# INLINE otoList #-}
src/Data/Sequences.hs view
@@ -7,10 +7,11 @@ module Data.Sequences where import Data.Maybe (fromJust, isJust)-import Data.Monoid (Monoid, mconcat, mempty)+import Data.Monoid (Monoid, mappend, mconcat, mempty) import Data.MonoTraversable import Data.Int (Int64, Int) import qualified Data.List as List+import qualified Data.List.Split as List import qualified Control.Monad (filterM, replicateM) import Prelude (Bool (..), Monad (..), Maybe (..), Ordering (..), Ord (..), Eq (..), Functor (..), fromIntegral, otherwise, (-), fst, snd, Integral, ($), flip, maybe, error) import Data.Char (Char, isSpace)@@ -60,8 +61,6 @@ -- @ intersperse :: Element seq -> seq -> seq - -- FIXME split :: (Element seq -> Bool) -> seq -> [seq]- -- | Reverse a sequence -- -- @@@ -411,6 +410,23 @@ unsafeIndex :: seq -> Index seq -> Element seq unsafeIndex = indexEx + -- | 'intercalate' @seq seqs@ inserts @seq@ in between @seqs@ and+ -- concatenates the result.+ --+ -- Since 0.9.3+ intercalate :: seq -> [seq] -> seq+ intercalate = defaultIntercalate++ -- | 'splitWhen' splits a sequence into components delimited by separators,+ -- where the predicate returns True for a separator element. The resulting+ -- components do not contain the separators. Two adjacent separators result+ -- in an empty component in the output. The number of resulting components+ -- is greater by one than number of separators.+ --+ -- Since 0.9.3+ splitWhen :: (Element seq -> Bool) -> seq -> [seq]+ splitWhen = defaultSplitWhen+ {-# INLINE fromList #-} {-# INLINE break #-} {-# INLINE span #-}@@ -440,6 +456,7 @@ {-# INLINE index #-} {-# INLINE indexEx #-} {-# INLINE unsafeIndex #-}+ {-# INLINE splitWhen #-} -- | Use "Data.List"'s implementation of 'Data.List.find'. defaultFind :: MonoFoldable seq => (Element seq -> Bool) -> seq -> Maybe (Element seq)@@ -461,6 +478,17 @@ defaultSortBy f = fromList . sortBy f . otoList {-# INLINE defaultSortBy #-} +-- | Default 'intercalate'+defaultIntercalate :: (IsSequence seq) => seq -> [seq] -> seq+defaultIntercalate _ [] = mempty+defaultIntercalate s (seq:seqs) = mconcat (seq : List.map (s `mappend`) seqs)+{-# INLINE defaultIntercalate #-}++-- | Use 'splitWhen' from "Data.List.Split"+defaultSplitWhen :: IsSequence seq => (Element seq -> Bool) -> seq -> [seq]+defaultSplitWhen f = List.map fromList . List.splitWhen f . otoList+{-# INLINE defaultSplitWhen #-}+ -- | Sort a vector using an supplied element ordering function. vectorSortBy :: VG.Vector v e => (e -> e -> Ordering) -> v e -> v e vectorSortBy f = VG.modify (VAM.sortBy f)@@ -538,6 +566,8 @@ where (matches, nonMatches) = partition ((== f head) . f) tail groupAllOn _ [] = []+ intercalate = List.intercalate+ splitWhen = List.splitWhen {-# INLINE fromList #-} {-# INLINE break #-} {-# INLINE span #-}@@ -564,6 +594,8 @@ {-# INLINE initEx #-} {-# INLINE unsafeTail #-} {-# INLINE unsafeInit #-}+ {-# INLINE intercalate #-}+ {-# INLINE splitWhen #-} instance SemiSequence (NE.NonEmpty a) where type Index (NE.NonEmpty a) = Int@@ -618,6 +650,9 @@ tailEx = S.tail initEx = S.init unsafeTail = SU.unsafeTail+ splitWhen f s | S.null s = [S.empty]+ | otherwise = S.splitWith f s+ intercalate = S.intercalate {-# INLINE fromList #-} {-# INLINE break #-} {-# INLINE span #-}@@ -644,6 +679,8 @@ {-# INLINE initEx #-} {-# INLINE unsafeTail #-} {-# INLINE unsafeInit #-}+ {-# INLINE splitWhen #-}+ {-# INLINE intercalate #-} index bs i | i >= S.length bs = Nothing@@ -688,6 +725,8 @@ groupBy = T.groupBy tailEx = T.tail initEx = T.init+ splitWhen = T.split+ intercalate = T.intercalate {-# INLINE fromList #-} {-# INLINE break #-} {-# INLINE span #-}@@ -714,6 +753,8 @@ {-# INLINE initEx #-} {-# INLINE unsafeTail #-} {-# INLINE unsafeInit #-}+ {-# INLINE splitWhen #-}+ {-# INLINE intercalate #-} index t i | i >= T.length t = Nothing@@ -758,6 +799,9 @@ groupBy = L.groupBy tailEx = L.tail initEx = L.init+ splitWhen f s | L.null s = [L.empty]+ | otherwise = L.splitWith f s+ intercalate = L.intercalate {-# INLINE fromList #-} {-# INLINE break #-} {-# INLINE span #-}@@ -784,6 +828,8 @@ {-# INLINE initEx #-} {-# INLINE unsafeTail #-} {-# INLINE unsafeInit #-}+ {-# INLINE splitWhen #-}+ {-# INLINE intercalate #-} indexEx = L.index unsafeIndex = L.index@@ -825,6 +871,8 @@ groupBy = TL.groupBy tailEx = TL.tail initEx = TL.init+ splitWhen = TL.split+ intercalate = TL.intercalate {-# INLINE fromList #-} {-# INLINE break #-} {-# INLINE span #-}@@ -851,6 +899,8 @@ {-# INLINE initEx #-} {-# INLINE unsafeTail #-} {-# INLINE unsafeInit #-}+ {-# INLINE splitWhen #-}+ {-# INLINE intercalate #-} indexEx = TL.index unsafeIndex = TL.index@@ -1198,6 +1248,42 @@ -- | A typeclass for sequences whose elements have the 'Eq' typeclass class (MonoFoldableEq seq, IsSequence seq, Eq (Element seq)) => EqSequence seq where++ -- | @'splitElem'@ splits a sequence into components delimited by separator+ -- element. It's equivalent to 'splitWhen' with equality predicate:+ --+ -- > splitElem sep === splitWhen (== sep)+ --+ -- Since 0.9.3+ splitElem :: Element seq -> seq -> [seq]+ splitElem x = splitWhen (== x)++ -- | @'splitSeq'@ splits a sequence into components delimited by+ -- separator subsequence. 'splitSeq' is the right inverse of 'intercalate':+ --+ -- > intercalate x . splitSeq x === id+ --+ -- 'splitElem' can be considered a special case of 'splitSeq'+ --+ -- > splitSeq (singleton sep) === splitElem sep+ --+ -- @'splitSeq' mempty@ is another special case: it splits just before each+ -- element, and in line with 'splitWhen' rules, it has at least one output+ -- component:+ --+ -- @+ -- > 'splitSeq' "" ""+ -- [""]+ -- > 'splitSeq' "" "a"+ -- ["", "a"]+ -- > 'splitSeq' "" "ab"+ -- ["", "a", "b"]+ -- @+ --+ -- Since 0.9.3+ splitSeq :: seq -> seq -> [seq]+ splitSeq = defaultSplitOn+ -- | 'stripPrefix' drops the given prefix from a sequence. -- It returns 'Nothing' if the sequence did not start with the prefix -- given, or 'Just' the sequence after the prefix, if it does.@@ -1249,6 +1335,8 @@ -- Equivalent to @'groupAllOn' id@ groupAll :: seq -> [seq] groupAll = groupAllOn id+ {-# INLINE splitElem #-}+ {-# INLINE splitSeq #-} {-# INLINE isPrefixOf #-} {-# INLINE isSuffixOf #-} {-# INLINE isInfixOf #-}@@ -1265,13 +1353,19 @@ notElem :: EqSequence seq => Element seq -> seq -> Bool notElem = onotElem +-- | Use 'splitOn' from "Data.List.Split"+defaultSplitOn :: EqSequence s => s -> s -> [s]+defaultSplitOn sep = List.map fromList . List.splitOn (otoList sep) . otoList+ instance Eq a => EqSequence [a] where+ splitSeq = List.splitOn stripPrefix = List.stripPrefix stripSuffix x y = fmap reverse (List.stripPrefix (reverse x) (reverse y)) group = List.group isPrefixOf = List.isPrefixOf isSuffixOf x y = List.isPrefixOf (List.reverse x) (List.reverse y) isInfixOf = List.isInfixOf+ {-# INLINE splitSeq #-} {-# INLINE stripPrefix #-} {-# INLINE stripSuffix #-} {-# INLINE group #-}@@ -1281,6 +1375,8 @@ {-# INLINE isInfixOf #-} instance EqSequence S.ByteString where+ splitElem sep s | S.null s = [S.empty]+ | otherwise = S.split sep s stripPrefix x y | x `S.isPrefixOf` y = Just (S.drop (S.length x) y) | otherwise = Nothing@@ -1291,6 +1387,7 @@ isPrefixOf = S.isPrefixOf isSuffixOf = S.isSuffixOf isInfixOf = S.isInfixOf+ {-# INLINE splitElem #-} {-# INLINE stripPrefix #-} {-# INLINE stripSuffix #-} {-# INLINE group #-}@@ -1300,6 +1397,8 @@ {-# INLINE isInfixOf #-} instance EqSequence L.ByteString where+ splitElem sep s | L.null s = [L.empty]+ | otherwise = L.split sep s stripPrefix x y | x `L.isPrefixOf` y = Just (L.drop (L.length x) y) | otherwise = Nothing@@ -1310,6 +1409,7 @@ isPrefixOf = L.isPrefixOf isSuffixOf = L.isSuffixOf isInfixOf x y = L.unpack x `List.isInfixOf` L.unpack y+ {-# INLINE splitElem #-} {-# INLINE stripPrefix #-} {-# INLINE stripSuffix #-} {-# INLINE group #-}@@ -1319,12 +1419,15 @@ {-# INLINE isInfixOf #-} instance EqSequence T.Text where+ splitSeq sep | T.null sep = (:) T.empty . List.map singleton . T.unpack+ | otherwise = T.splitOn sep stripPrefix = T.stripPrefix stripSuffix = T.stripSuffix group = T.group isPrefixOf = T.isPrefixOf isSuffixOf = T.isSuffixOf isInfixOf = T.isInfixOf+ {-# INLINE splitSeq #-} {-# INLINE stripPrefix #-} {-# INLINE stripSuffix #-} {-# INLINE group #-}@@ -1334,12 +1437,15 @@ {-# INLINE isInfixOf #-} instance EqSequence TL.Text where+ splitSeq sep | TL.null sep = (:) TL.empty . List.map singleton . TL.unpack+ | otherwise = TL.splitOn sep stripPrefix = TL.stripPrefix stripSuffix = TL.stripSuffix group = TL.group isPrefixOf = TL.isPrefixOf isSuffixOf = TL.isSuffixOf isInfixOf = TL.isInfixOf+ {-# INLINE splitSeq #-} {-# INLINE stripPrefix #-} {-# INLINE stripSuffix #-} {-# INLINE group #-}
test/Spec.hs view
@@ -1,6 +1,8 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ViewPatterns #-}+ module Spec where import Data.MonoTraversable@@ -9,11 +11,14 @@ import qualified Data.Sequence as Seq import qualified Data.NonNull as NN import Data.ByteVector+import Data.Monoid (mempty, mconcat)+import Data.Maybe (fromMaybe) import Test.Hspec import Test.Hspec.QuickCheck import Test.HUnit ((@?=)) import Test.QuickCheck hiding (NonEmptyList(..))+import qualified Test.QuickCheck as QC import qualified Test.QuickCheck.Modifiers as QCM import Data.Text (Text)@@ -38,7 +43,7 @@ import Control.Monad.Trans.Writer import Prelude (Bool (..), ($), IO, min, abs, Eq (..), (&&), fromIntegral, Ord (..), String, mod, Int, Integer, show,- return, asTypeOf, (.), Show, id, (+), succ, Maybe (..), (*), mod, map, flip, otherwise, (-), div, seq)+ return, asTypeOf, (.), Show, id, (+), succ, Maybe (..), (*), mod, map, flip, otherwise, (-), div, seq, maybe) import qualified Prelude instance Arbitrary a => Arbitrary (NE.NonEmpty a) where@@ -385,6 +390,59 @@ test "Strict Text" T.empty test "Lazy Text" TL.empty + describe "Intercalate" $ do+ let test typ dummy = describe typ $ do+ prop "intercalate === defaultIntercalate" $ \list lists ->+ let seq = fromListAs list dummy+ seqs = map (`fromListAs` dummy) lists+ in intercalate seq seqs @?= defaultIntercalate seq seqs+ test "List" ([] :: [Int])+ test "Vector" (V.empty :: V.Vector Int)+ test "Storable Vector" (VS.empty :: VS.Vector Int)+ test "Unboxed Vector" (U.empty :: U.Vector Int)+ test "Strict ByteString" S.empty+ test "Lazy ByteString" L.empty+ test "Strict Text" T.empty+ test "Lazy Text" TL.empty++ describe "Splitting" $ do+ let test typ dummy = describe typ $ do+ let fromList' = (`fromListAs` dummy)+ let fromSepList sep = fromList' . map (fromMaybe sep)+ prop "intercalate sep . splitSeq sep === id" $+ \(fromList' -> sep) ->+ \(mconcat . map (maybe sep fromList') -> xs) ->+ intercalate sep (splitSeq sep xs) @?= xs+ prop "splitSeq mempty xs === mempty : map singleton (otoList xs)" $+ \input ->+ splitSeq mempty (fromList' input) @?= mempty : map singleton input+ prop "splitSeq _ mempty == [mempty]" $+ \(fromList' -> sep) ->+ splitSeq sep mempty @?= [mempty]+ prop "intercalate (singleton sep) . splitElem sep === id" $+ \sep -> \(fromSepList sep -> xs) ->+ intercalate (singleton sep) (splitElem sep xs) @?= xs+ prop "length . splitElem sep === succ . length . filter (== sep)" $+ \sep -> \(fromSepList sep -> xs) ->+ olength (splitElem sep xs) @?= olength (filter (== sep) xs) + 1+ prop "splitElem sep (replicate n sep) == replicate (n+1) mempty" $+ \(NonNegative n) sep ->+ splitElem sep (fromList' (replicate n sep)) @?= replicate (n + 1) mempty+ prop "splitElem sep === splitWhen (== sep)" $+ \sep -> \(fromSepList sep -> xs) ->+ splitElem sep xs @?= splitWhen (== sep) xs+ prop "splitElem sep === splitSeq (singleton sep)" $+ \sep -> \(fromSepList sep -> xs) ->+ splitElem sep xs @?= splitSeq (singleton sep) xs+ test "List" ([] :: [Int])+ test "Vector" (V.empty :: V.Vector Int)+ test "Storable Vector" (VS.empty :: VS.Vector Int)+ test "Unboxed Vector" (U.empty :: U.Vector Int)+ test "Strict ByteString" S.empty+ test "Lazy ByteString" L.empty+ test "Strict Text" T.empty+ test "Lazy Text" TL.empty+ describe "Data.ByteVector" $ do prop "toByteVector" $ \ws -> (otoList . toByteVector . fromList $ ws) @?= ws@@ -398,3 +456,4 @@ it "#31 find doesn't infinitely loop on NonEmpty" $ find (== "a") ("a" NE.:| ["d","fgf"]) @?= Just "a"+