Skip to content

Commit 27b9277

Browse files
committed
Merge pull request #179 from treeowl/traverseWithIndex
Add traverseWithIndex
2 parents bf0bee9 + 29919f2 commit 27b9277

File tree

3 files changed

+129
-2
lines changed

3 files changed

+129
-2
lines changed

Data/Sequence.hs

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ module Data.Sequence (
139139
foldrWithIndex, -- :: (Int -> a -> b -> b) -> b -> Seq a -> b
140140
-- * Transformations
141141
mapWithIndex, -- :: (Int -> a -> b) -> Seq a -> Seq b
142+
traverseWithIndex, -- :: Applicative f => (Int -> a -> f b) -> Seq a -> f (Seq b)
142143
reverse, -- :: Seq a -> Seq a
143144
-- ** Zips
144145
zip, -- :: Seq a -> Seq b -> Seq (a, b)
@@ -209,6 +210,7 @@ import qualified GHC.Exts
209210
import Data.Functor.Identity (Identity(..))
210211
#endif
211212

213+
default ()
212214

213215
infixr 5 `consTree`
214216
infixl 5 `snocTree`
@@ -1659,6 +1661,123 @@ mapWithIndex f' (Seq xs') = Seq $ mapWithIndexTree (\s (Elem a) -> Elem (f' s a)
16591661
#-}
16601662
#endif
16611663

1664+
-- | 'traverseWithIndex' is a version of 'traverse' that also offers
1665+
-- access to the index of each element.
1666+
traverseWithIndex :: Applicative f => (Int -> a -> f b) -> Seq a -> f (Seq b)
1667+
traverseWithIndex f' (Seq xs') = Seq <$> traverseWithIndexTreeE (\s (Elem a) -> Elem <$> f' s a) 0 xs'
1668+
where
1669+
-- We have to specialize these functions by hand, unfortunately, because
1670+
-- GHC does not specialize until *all* instances are determined.
1671+
-- If we tried to used the Sized trick, it would likely leak to runtime.
1672+
traverseWithIndexTreeE :: Applicative f => (Int -> Elem a -> f b) -> Int -> FingerTree (Elem a) -> f (FingerTree b)
1673+
traverseWithIndexTreeE _ s Empty = s `seq` pure Empty
1674+
traverseWithIndexTreeE f s (Single xs) = Single <$> f s xs
1675+
traverseWithIndexTreeE f s (Deep n pr m sf) = sPspr `seq` sPsprm `seq`
1676+
Deep n <$>
1677+
traverseWithIndexDigitE f s pr <*>
1678+
traverseWithIndexTreeN (traverseWithIndexNodeE f) sPspr m <*>
1679+
traverseWithIndexDigitE f sPsprm sf
1680+
where
1681+
sPspr = s + size pr
1682+
sPsprm = s + n - size sf
1683+
1684+
traverseWithIndexTreeN :: Applicative f => (Int -> Node a -> f b) -> Int -> FingerTree (Node a) -> f (FingerTree b)
1685+
traverseWithIndexTreeN _ s Empty = s `seq` pure Empty
1686+
traverseWithIndexTreeN f s (Single xs) = Single <$> f s xs
1687+
traverseWithIndexTreeN f s (Deep n pr m sf) = sPspr `seq` sPsprm `seq`
1688+
Deep n <$>
1689+
traverseWithIndexDigitN f s pr <*>
1690+
traverseWithIndexTreeN (traverseWithIndexNodeN f) sPspr m <*>
1691+
traverseWithIndexDigitN f sPsprm sf
1692+
where
1693+
sPspr = s + size pr
1694+
sPsprm = s + n - size sf
1695+
1696+
traverseWithIndexDigitE :: Applicative f => (Int -> Elem a -> f b) -> Int -> Digit (Elem a) -> f (Digit b)
1697+
traverseWithIndexDigitE f s (One a) = One <$> f s a
1698+
traverseWithIndexDigitE f s (Two a b) = sPsa `seq` Two <$> f s a <*> f sPsa b
1699+
where
1700+
sPsa = s + size a
1701+
traverseWithIndexDigitE f s (Three a b c) = sPsa `seq` sPsab `seq`
1702+
Three <$> f s a <*> f sPsa b <*> f sPsab c
1703+
where
1704+
sPsa = s + size a
1705+
sPsab = sPsa + size b
1706+
traverseWithIndexDigitE f s (Four a b c d) = sPsa `seq` sPsab `seq` sPsabc `seq`
1707+
Four <$> f s a <*> f sPsa b <*> f sPsab c <*> f sPsabc d
1708+
where
1709+
sPsa = s + size a
1710+
sPsab = sPsa + size b
1711+
sPsabc = sPsab + size c
1712+
1713+
traverseWithIndexDigitN :: Applicative f => (Int -> Node a -> f b) -> Int -> Digit (Node a) -> f (Digit b)
1714+
traverseWithIndexDigitN f s (One a) = One <$> f s a
1715+
traverseWithIndexDigitN f s (Two a b) = sPsa `seq` Two <$> f s a <*> f sPsa b
1716+
where
1717+
sPsa = s + size a
1718+
traverseWithIndexDigitN f s (Three a b c) = sPsa `seq` sPsab `seq`
1719+
Three <$> f s a <*> f sPsa b <*> f sPsab c
1720+
where
1721+
sPsa = s + size a
1722+
sPsab = sPsa + size b
1723+
traverseWithIndexDigitN f s (Four a b c d) = sPsa `seq` sPsab `seq` sPsabc `seq`
1724+
Four <$> f s a <*> f sPsa b <*> f sPsab c <*> f sPsabc d
1725+
where
1726+
sPsa = s + size a
1727+
sPsab = sPsa + size b
1728+
sPsabc = sPsab + size c
1729+
1730+
traverseWithIndexNodeE :: Applicative f => (Int -> Elem a -> f b) -> Int -> Node (Elem a) -> f (Node b)
1731+
traverseWithIndexNodeE f s (Node2 ns a b) = sPsa `seq` Node2 ns <$> f s a <*> f sPsa b
1732+
where
1733+
sPsa = s + size a
1734+
traverseWithIndexNodeE f s (Node3 ns a b c) = sPsa `seq` sPsab `seq`
1735+
Node3 ns <$> f s a <*> f sPsa b <*> f sPsab c
1736+
where
1737+
sPsa = s + size a
1738+
sPsab = sPsa + size b
1739+
1740+
traverseWithIndexNodeN :: Applicative f => (Int -> Node a -> f b) -> Int -> Node (Node a) -> f (Node b)
1741+
traverseWithIndexNodeN f s (Node2 ns a b) = sPsa `seq` Node2 ns <$> f s a <*> f sPsa b
1742+
where
1743+
sPsa = s + size a
1744+
traverseWithIndexNodeN f s (Node3 ns a b c) = sPsa `seq` sPsab `seq`
1745+
Node3 ns <$> f s a <*> f sPsa b <*> f sPsab c
1746+
where
1747+
sPsa = s + size a
1748+
sPsab = sPsa + size b
1749+
1750+
{-# NOINLINE [1] traverseWithIndex #-}
1751+
#ifdef __GLASGOW_HASKELL__
1752+
{-# RULES
1753+
"travWithIndex/mapWithIndex" forall f g xs . traverseWithIndex f (mapWithIndex g xs) =
1754+
traverseWithIndex (\k a -> f k (g k a)) xs
1755+
"travWithIndex/fmapSeq" forall f g xs . traverseWithIndex f (fmapSeq g xs) =
1756+
traverseWithIndex (\k a -> f k (g a)) xs
1757+
#-}
1758+
#endif
1759+
{-
1760+
It might be nice to be able to rewrite
1761+
1762+
traverseWithIndex f (fromFunction i g)
1763+
to
1764+
replicateAWithIndex i (\k -> f k (g k))
1765+
and
1766+
traverse f (fromFunction i g)
1767+
to
1768+
replicateAWithIndex i (f . g)
1769+
1770+
but we don't have replicateAWithIndex as yet.
1771+
1772+
We might wish for a rule like
1773+
"fmapSeq/travWithIndex" forall f g xs . fmapSeq f <$> traverseWithIndex g xs =
1774+
traverseWithIndex (\k a -> f <$> g k a) xs
1775+
Unfortunately, this rule could screw up the inliner's treatment of
1776+
fmap in general, and it also relies on the arbitrary Functor being
1777+
valid.
1778+
-}
1779+
1780+
16621781
-- | /O(n)/. Convert a given sequence length and a function representing that
16631782
-- sequence into a sequence.
16641783
fromFunction :: Int -> (Int -> a) -> Seq a

containers.cabal

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,8 @@ Test-suite seq-properties
216216
build-depends:
217217
QuickCheck,
218218
test-framework,
219-
test-framework-quickcheck2
219+
test-framework-quickcheck2,
220+
transformers
220221

221222
test-suite map-strictness-properties
222223
hs-source-dirs: tests, .

tests/seq-properties.hs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import Data.Sequence -- needs to be compiled with -DTESTING for use here
22

33
import Control.Applicative (Applicative(..))
44
import Control.Arrow ((***))
5+
import Control.Monad.Trans.State.Strict
56
import Data.Array (listArray)
67
import Data.Foldable (Foldable(foldl, foldl1, foldr, foldr1, foldMap), toList, all, sum)
78
import Data.Functor ((<$>), (<$))
@@ -87,6 +88,7 @@ main = defaultMain
8788
, testProperty "foldlWithIndex" prop_foldlWithIndex
8889
, testProperty "foldrWithIndex" prop_foldrWithIndex
8990
, testProperty "mapWithIndex" prop_mapWithIndex
91+
, testProperty "traverseWithIndex" prop_traverseWithIndex
9092
, testProperty "reverse" prop_reverse
9193
, testProperty "zip" prop_zip
9294
, testProperty "zipWith" prop_zipWith
@@ -113,7 +115,7 @@ instance Arbitrary a => Arbitrary (Elem a) where
113115
instance (Arbitrary a, Sized a) => Arbitrary (FingerTree a) where
114116
arbitrary = sized arb
115117
where
116-
arb :: (Arbitrary a, Sized a) => Int -> Gen (FingerTree a)
118+
arb :: (Arbitrary b, Sized b) => Int -> Gen (FingerTree b)
117119
arb 0 = return Empty
118120
arb 1 = Single <$> arbitrary
119121
arb n = do
@@ -559,6 +561,11 @@ prop_mapWithIndex xs =
559561
toList' (mapWithIndex f xs) ~= map (uncurry f) (Data.List.zip [0..] (toList xs))
560562
where f = (,)
561563

564+
prop_traverseWithIndex :: Seq Int -> Bool
565+
prop_traverseWithIndex xs =
566+
runState (traverseWithIndex (\i x -> modify ((i,x) :)) xs) [] ==
567+
runState (sequenceA . mapWithIndex (\i x -> modify ((i,x) :)) $ xs) []
568+
562569
prop_reverse :: Seq A -> Bool
563570
prop_reverse xs =
564571
toList' (reverse xs) ~= Prelude.reverse (toList xs)

0 commit comments

Comments
 (0)