Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Legalize scanl1/scanr1 over an empty vector #382

Merged
merged 2 commits into from
May 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions vector/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@
`Data.Vector.Storable{.Mutable}` to allow this (the onus is on the user
to ensure that no `Storable` invariants are broken when using these
functions).
* The functions `scanl1`, `scanl1'`, `scanr1`, and `scanr1'` for immutable
vectors are now defined when given empty vectors as arguments,
in which case they return empty vectors. This new behavior is consistent
with the one of the corresponding functions in `Data.List`.
Prior to this change, applying an empty vector to any of those functions
resulted in an error. This change was introduced in:
[#382](https://github.com/haskell/vector/pull/382)


# Changes in version 0.12.3.0

Expand Down
40 changes: 36 additions & 4 deletions vector/src/Data/Vector.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1929,17 +1929,37 @@ iscanl' :: (Int -> a -> b -> a) -> a -> Vector b -> Vector a
{-# INLINE iscanl' #-}
iscanl' = G.iscanl'

-- | /O(n)/ Scan over a non-empty vector
-- | /O(n)/ Initial-value free scan over a vector
--
-- > scanl f <x1,...,xn> = <y1,...,yn>
-- > where y1 = x1
-- > yi = f y(i-1) xi
--
-- Note: Since 0.13, application of this to an empty vector no longer
-- results in an error; instead produces an empty vector.
--
-- ==== __Examples__
-- >>> import qualified Data.Vector as V
-- >>> V.scanl1 min $ V.fromListN 5 [4,2,4,1,3 :: Int]
-- [4,2,2,1,1]
-- >>> V.scanl1 max $ V.fromListN 5 [1,3,2,5,4 :: Int]
-- [1,3,3,5,5]
-- >>> V.scanl1 min (V.empty :: V.Vector Int)
-- []
scanl1 :: (a -> a -> a) -> Vector a -> Vector a
{-# INLINE scanl1 #-}
scanl1 = G.scanl1

-- | /O(n)/ Scan over a non-empty vector with a strict accumulator
-- | /O(n)/ Initial-value free scan over a vector with a strict accumulator
--
-- ==== __Examples__
-- >>> import qualified Data.Vector as V
-- >>> V.scanl1' min $ V.fromListN 5 [4,2,4,1,3 :: Int]
-- [4,2,2,1,1]
-- >>> V.scanl1' max $ V.fromListN 5 [1,3,2,5,4 :: Int]
-- [1,3,3,5,5]
-- >>> V.scanl1' min (V.empty :: V.Vector Int)
-- []
scanl1' :: (a -> a -> a) -> Vector a -> Vector a
{-# INLINE scanl1' #-}
scanl1' = G.scanl1'
Expand Down Expand Up @@ -1993,13 +2013,25 @@ iscanr' :: (Int -> a -> b -> b) -> b -> Vector a -> Vector b
{-# INLINE iscanr' #-}
iscanr' = G.iscanr'

-- | /O(n)/ Right-to-left scan over a non-empty vector
-- | /O(n)/ Right-to-left, initial-value free scan over a vector
scanr1 :: (a -> a -> a) -> Vector a -> Vector a
{-# INLINE scanr1 #-}
scanr1 = G.scanr1

-- | /O(n)/ Right-to-left scan over a non-empty vector with a strict
-- | /O(n)/ Right-to-left, initial-value free scan over a vector with a strict
-- accumulator
--
-- Note: Since 0.13, application of this to an empty vector no longer
-- results in an error; instead produces an empty vector.
--
-- ==== __Examples__
-- >>> import qualified Data.Vector as V
-- >>> V.scanr1' min $ V.fromListN 5 [3,1,4,2,4 :: Int]
-- [1,1,2,2,4]
-- >>> V.scanr1' max $ V.fromListN 5 [4,5,2,3,1 :: Int]
-- [5,5,3,3,1]
-- >>> V.scanr1' min (V.empty :: V.Vector Int)
-- []
scanr1' :: (a -> a -> a) -> Vector a -> Vector a
{-# INLINE scanr1' #-}
scanr1' = G.scanr1'
Expand Down
4 changes: 2 additions & 2 deletions vector/src/Data/Vector/Fusion/Bundle.hs
Original file line number Diff line number Diff line change
Expand Up @@ -475,12 +475,12 @@ scanl' :: (a -> b -> a) -> a -> Bundle v b -> Bundle v a
{-# INLINE scanl' #-}
scanl' = M.scanl'

-- | Scan over a non-empty 'Bundle'
-- | Initial-value free scan over a 'Bundle'
scanl1 :: (a -> a -> a) -> Bundle v a -> Bundle v a
{-# INLINE scanl1 #-}
scanl1 = M.scanl1

-- | Scan over a non-empty 'Bundle' with a strict accumulator
-- | Initial-value free scan over a 'Bundle' with a strict accumulator
scanl1' :: (a -> a -> a) -> Bundle v a -> Bundle v a
{-# INLINE scanl1' #-}
scanl1' = M.scanl1'
Expand Down
10 changes: 5 additions & 5 deletions vector/src/Data/Vector/Fusion/Bundle/Monadic.hs
Original file line number Diff line number Diff line change
Expand Up @@ -749,23 +749,23 @@ scanlM' :: Monad m => (a -> b -> m a) -> a -> Bundle m v b -> Bundle m v a
{-# INLINE scanlM' #-}
scanlM' f z s = z `seq` (z `cons` postscanlM f z s)

-- | Scan over a non-empty 'Bundle'
-- | Initial-value free scan over a 'Bundle'
scanl1 :: Monad m => (a -> a -> a) -> Bundle m v a -> Bundle m v a
{-# INLINE scanl1 #-}
scanl1 f = scanl1M (\x y -> return (f x y))

-- | Scan over a non-empty 'Bundle' with a monadic operator
-- | Initial-value free scan over a 'Bundle' with a monadic operator
scanl1M :: Monad m => (a -> a -> m a) -> Bundle m v a -> Bundle m v a
{-# INLINE_FUSED scanl1M #-}
scanl1M f Bundle{sElems = s, sSize = sz} = fromStream (S.scanl1M f s) sz

-- | Scan over a non-empty 'Bundle' with a strict accumulator
-- | Initial-value free scan over a 'Bundle' with a strict accumulator
scanl1' :: Monad m => (a -> a -> a) -> Bundle m v a -> Bundle m v a
{-# INLINE scanl1' #-}
scanl1' f = scanl1M' (\x y -> return (f x y))

-- | Scan over a non-empty 'Bundle' with a strict accumulator and a monadic
-- operator
-- | Initial-value free scan over a 'Bundle' with a strict accumulator
-- and a monadic operator
scanl1M' :: Monad m => (a -> a -> m a) -> Bundle m v a -> Bundle m v a
{-# INLINE_FUSED scanl1M' #-}
scanl1M' f Bundle{sElems = s, sSize = sz} = fromStream (S.scanl1M' f s) sz
Expand Down
14 changes: 7 additions & 7 deletions vector/src/Data/Vector/Fusion/Stream/Monadic.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1262,12 +1262,12 @@ scanlM' :: Monad m => (a -> b -> m a) -> a -> Stream m b -> Stream m a
{-# INLINE scanlM' #-}
scanlM' f z s = z `seq` (z `cons` postscanlM f z s)

-- | Scan over a non-empty 'Stream'
-- | Initial-value free scan over a 'Stream'
scanl1 :: Monad m => (a -> a -> a) -> Stream m a -> Stream m a
{-# INLINE scanl1 #-}
scanl1 f = scanl1M (\x y -> return (f x y))

-- | Scan over a non-empty 'Stream' with a monadic operator
-- | Initial-value free scan over a 'Stream' with a monadic operator
scanl1M :: Monad m => (a -> a -> m a) -> Stream m a -> Stream m a
{-# INLINE_FUSED scanl1M #-}
scanl1M f (Stream step t) = Stream step' (t, Nothing)
Expand All @@ -1278,7 +1278,7 @@ scanl1M f (Stream step t) = Stream step' (t, Nothing)
case r of
Yield x s' -> return $ Yield x (s', Just x)
Skip s' -> return $ Skip (s', Nothing)
Done -> EMPTY_STREAM "scanl1M"
Done -> return Done

step' (s, Just x) = do
r <- step s
Expand All @@ -1289,13 +1289,13 @@ scanl1M f (Stream step t) = Stream step' (t, Nothing)
Skip s' -> return $ Skip (s', Just x)
Done -> return Done

-- | Scan over a non-empty 'Stream' with a strict accumulator
-- | Initial-value free scan over a 'Stream' with a strict accumulator
scanl1' :: Monad m => (a -> a -> a) -> Stream m a -> Stream m a
{-# INLINE scanl1' #-}
scanl1' f = scanl1M' (\x y -> return (f x y))

-- | Scan over a non-empty 'Stream' with a strict accumulator and a monadic
-- operator
-- | Initial-value free scan over a 'Stream' with a strict accumulator
-- and a monadic operator
scanl1M' :: Monad m => (a -> a -> m a) -> Stream m a -> Stream m a
{-# INLINE_FUSED scanl1M' #-}
scanl1M' f (Stream step t) = Stream step' (t, Nothing)
Expand All @@ -1306,7 +1306,7 @@ scanl1M' f (Stream step t) = Stream step' (t, Nothing)
case r of
Yield x s' -> x `seq` return (Yield x (s', Just x))
Skip s' -> return $ Skip (s', Nothing)
Done -> EMPTY_STREAM "scanl1M"
Done -> return Done

step' (s, Just x) = x `seq`
do
Expand Down
55 changes: 51 additions & 4 deletions vector/src/Data/Vector/Generic.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2111,17 +2111,40 @@ iscanl' f z =
. stream


-- | /O(n)/ Scan over a non-empty vector
-- | /O(n)/ Initial-value free scan over a vector
--
-- > scanl f <x1,...,xn> = <y1,...,yn>
-- > where y1 = x1
-- > yi = f y(i-1) xi
--
-- Note: Since 0.13, application of this to an empty vector no longer
-- results in an error; instead produces an empty vector.
--
-- ==== __Examples__
-- >>> import qualified Data.Vector as V
-- >>> V.scanl1 min $ V.fromListN 5 [4,2,4,1,3 :: Int]
-- [4,2,2,1,1]
-- >>> V.scanl1 max $ V.fromListN 5 [1,3,2,5,4 :: Int]
-- [1,3,3,5,5]
-- >>> V.scanl1 min (V.empty :: V.Vector Int)
-- []
scanl1 :: Vector v a => (a -> a -> a) -> v a -> v a
{-# INLINE scanl1 #-}
scanl1 f = unstream . inplace (S.scanl1 f) id . stream

-- | /O(n)/ Scan over a non-empty vector with a strict accumulator
-- | /O(n)/ Initial-value free scan over a vector with a strict accumulator
--
-- Note: Since 0.13, application of this to an empty vector no longer
-- results in an error; instead produces an empty vector.
--
-- ==== __Examples__
-- >>> import qualified Data.Vector as V
-- >>> V.scanl1' min $ V.fromListN 5 [4,2,4,1,3 :: Int]
-- [4,2,2,1,1]
-- >>> V.scanl1' max $ V.fromListN 5 [1,3,2,5,4 :: Int]
-- [1,3,3,5,5]
-- >>> V.scanl1' min (V.empty :: V.Vector Int)
-- []
scanl1' :: Vector v a => (a -> a -> a) -> v a -> v a
{-# INLINE scanl1' #-}
scanl1' f = unstream . inplace (S.scanl1' f) id . stream
Expand Down Expand Up @@ -2181,13 +2204,37 @@ iscanr' f z v =
$ v
where n = length v

-- | /O(n)/ Right-to-left scan over a non-empty vector
-- | /O(n)/ Right-to-left, initial-value free scan over a vector
--
-- Note: Since 0.13, application of this to an empty vector no longer
-- results in an error; instead produces an empty vector.
--
-- ==== __Examples__
-- >>> import qualified Data.Vector as V
-- >>> V.scanr1 min $ V.fromListN 5 [3,1,4,2,4 :: Int]
-- [1,1,2,2,4]
-- >>> V.scanr1 max $ V.fromListN 5 [4,5,2,3,1 :: Int]
-- [5,5,3,3,1]
-- >>> V.scanr1 min (V.empty :: V.Vector Int)
-- []
scanr1 :: Vector v a => (a -> a -> a) -> v a -> v a
{-# INLINE scanr1 #-}
scanr1 f = unstreamR . inplace (S.scanl1 (flip f)) id . streamR

-- | /O(n)/ Right-to-left scan over a non-empty vector with a strict
-- | /O(n)/ Right-to-left, initial-value free scan over a vector with a strict
-- accumulator
--
-- Note: Since 0.13, application of this to an empty vector no longer
-- results in an error; instead produces an empty vector.
--
-- ==== __Examples__
-- >>> import qualified Data.Vector as V
-- >>> V.scanr1' min $ V.fromListN 5 [3,1,4,2,4 :: Int]
-- [1,1,2,2,4]
-- >>> V.scanr1' max $ V.fromListN 5 [4,5,2,3,1 :: Int]
-- [5,5,3,3,1]
-- >>> V.scanr1' min (V.empty :: V.Vector Int)
-- []
scanr1' :: Vector v a => (a -> a -> a) -> v a -> v a
{-# INLINE scanr1' #-}
scanr1' f = unstreamR . inplace (S.scanl1' (flip f)) id . streamR
Expand Down
40 changes: 36 additions & 4 deletions vector/src/Data/Vector/Primitive.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1582,17 +1582,37 @@ iscanl' :: (Prim a, Prim b) => (Int -> a -> b -> a) -> a -> Vector b -> Vector a
iscanl' = G.iscanl'


-- | /O(n)/ Scan over a non-empty vector
-- | /O(n)/ Initial-value free scan over a vector
--
-- > scanl f <x1,...,xn> = <y1,...,yn>
-- > where y1 = x1
-- > yi = f y(i-1) xi
--
-- Note: Since 0.13, application of this to an empty vector no longer
-- results in an error; instead produces an empty vector.
--
-- ==== __Examples__
-- >>> import qualified Data.Vector.Primitive as VP
-- >>> VP.scanl1 min $ VP.fromListN 5 [4,2,4,1,3 :: Int]
-- [4,2,2,1,1]
-- >>> VP.scanl1 max $ VP.fromListN 5 [1,3,2,5,4 :: Int]
-- [1,3,3,5,5]
-- >>> VP.scanl1 min (VP.empty :: VP.Vector Int)
-- []
scanl1 :: Prim a => (a -> a -> a) -> Vector a -> Vector a
{-# INLINE scanl1 #-}
scanl1 = G.scanl1

-- | /O(n)/ Scan over a non-empty vector with a strict accumulator
-- | /O(n)/ Initial-value free scan over a vector with a strict accumulator
--
-- ==== __Examples__
-- >>> import qualified Data.Vector.Primitive as VP
-- >>> VP.scanl1' min $ VP.fromListN 5 [4,2,4,1,3 :: Int]
-- [4,2,2,1,1]
-- >>> VP.scanl1' max $ VP.fromListN 5 [1,3,2,5,4 :: Int]
-- [1,3,3,5,5]
-- >>> VP.scanl1' min (VP.empty :: VP.Vector Int)
-- []
scanl1' :: Prim a => (a -> a -> a) -> Vector a -> Vector a
{-# INLINE scanl1' #-}
scanl1' = G.scanl1'
Expand Down Expand Up @@ -1646,13 +1666,25 @@ iscanr' :: (Prim a, Prim b) => (Int -> a -> b -> b) -> b -> Vector a -> Vector b
{-# INLINE iscanr' #-}
iscanr' = G.iscanr'

-- | /O(n)/ Right-to-left scan over a non-empty vector
-- | /O(n)/ Right-to-left, initial-value free scan over a vector
scanr1 :: Prim a => (a -> a -> a) -> Vector a -> Vector a
{-# INLINE scanr1 #-}
scanr1 = G.scanr1

-- | /O(n)/ Right-to-left scan over a non-empty vector with a strict
-- | /O(n)/ Right-to-left, initial-value free scan over a vector with a strict
-- accumulator
--
-- Note: Since 0.13, application of this to an empty vector no longer
-- results in an error; instead produces an empty vector.
--
-- ==== __Examples__
-- >>> import qualified Data.Vector.Primitive as VP
-- >>> VP.scanr1' min $ VP.fromListN 5 [3,1,4,2,4 :: Int]
-- [1,1,2,2,4]
-- >>> VP.scanr1' max $ VP.fromListN 5 [4,5,2,3,1 :: Int]
-- [5,5,3,3,1]
-- >>> VP.scanr1' min (VP.empty :: VP.Vector Int)
-- []
scanr1' :: Prim a => (a -> a -> a) -> Vector a -> Vector a
{-# INLINE scanr1' #-}
scanr1' = G.scanr1'
Expand Down
40 changes: 36 additions & 4 deletions vector/src/Data/Vector/Storable.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1628,17 +1628,37 @@ iscanl' :: (Storable a, Storable b) => (Int -> a -> b -> a) -> a -> Vector b ->
{-# INLINE iscanl' #-}
iscanl' = G.iscanl'

-- | /O(n)/ Scan over a non-empty vector
-- | /O(n)/ Initial-value free scan over a vector
--
-- > scanl f <x1,...,xn> = <y1,...,yn>
-- > where y1 = x1
-- > yi = f y(i-1) xi
--
-- Note: Since 0.13, application of this to an empty vector no longer
-- results in an error; instead produces an empty vector.
--
-- ==== __Examples__
-- >>> import qualified Data.Vector.Storable as VS
-- >>> VS.scanl1 min $ VS.fromListN 5 [4,2,4,1,3 :: Int]
-- [4,2,2,1,1]
-- >>> VS.scanl1 max $ VS.fromListN 5 [1,3,2,5,4 :: Int]
-- [1,3,3,5,5]
-- >>> VS.scanl1 min (VS.empty :: VS.Vector Int)
-- []
scanl1 :: Storable a => (a -> a -> a) -> Vector a -> Vector a
{-# INLINE scanl1 #-}
scanl1 = G.scanl1

-- | /O(n)/ Scan over a non-empty vector with a strict accumulator
-- | /O(n)/ Initial-value free scan over a vector with a strict accumulator
--
-- ==== __Examples__
-- >>> import qualified Data.Vector.Storable as VS
-- >>> VS.scanl1' min $ VS.fromListN 5 [4,2,4,1,3 :: Int]
-- [4,2,2,1,1]
-- >>> VS.scanl1' max $ VS.fromListN 5 [1,3,2,5,4 :: Int]
-- [1,3,3,5,5]
-- >>> VS.scanl1' min (VS.empty :: VS.Vector Int)
-- []
scanl1' :: Storable a => (a -> a -> a) -> Vector a -> Vector a
{-# INLINE scanl1' #-}
scanl1' = G.scanl1'
Expand Down Expand Up @@ -1692,13 +1712,25 @@ iscanr' :: (Storable a, Storable b) => (Int -> a -> b -> b) -> b -> Vector a ->
{-# INLINE iscanr' #-}
iscanr' = G.iscanr'

-- | /O(n)/ Right-to-left scan over a non-empty vector
-- | /O(n)/ Right-to-left, initial-value free scan over a vector
scanr1 :: Storable a => (a -> a -> a) -> Vector a -> Vector a
{-# INLINE scanr1 #-}
scanr1 = G.scanr1

-- | /O(n)/ Right-to-left scan over a non-empty vector with a strict
-- | /O(n)/ Right-to-left, initial-value free scan over a vector with a strict
-- accumulator
--
-- Note: Since 0.13, application of this to an empty vector no longer
-- results in an error; instead produces an empty vector.
--
-- ==== __Examples__
-- >>> import qualified Data.Vector.Storable as VS
-- >>> VS.scanr1' min $ VS.fromListN 5 [3,1,4,2,4 :: Int]
-- [1,1,2,2,4]
-- >>> VS.scanr1' max $ VS.fromListN 5 [4,5,2,3,1 :: Int]
-- [5,5,3,3,1]
-- >>> VS.scanr1' min (VS.empty :: VS.Vector Int)
-- []
scanr1' :: Storable a => (a -> a -> a) -> Vector a -> Vector a
{-# INLINE scanr1' #-}
scanr1' = G.scanr1'
Expand Down
Loading