diff --git a/vector-bench-papi/benchmarks/Main.hs b/vector-bench-papi/benchmarks/Main.hs index 590a7573..284e557f 100644 --- a/vector-bench-papi/benchmarks/Main.hs +++ b/vector-bench-papi/benchmarks/Main.hs @@ -12,6 +12,8 @@ import Bench.Vector.Algo.Spectral (spectral) import Bench.Vector.Algo.Tridiag (tridiag) import Bench.Vector.Algo.FindIndexR (findIndexR, findIndexR_naive, findIndexR_manual) import Bench.Vector.Algo.NextPermutation (generatePermTests) +import Bench.Vector.Algo.Applicative ( generateState, generateStateUnfold, generateIO, generateIOPrim + , lensSum, lensMap, baselineSum, baselineMap) import Bench.Vector.TestData.ParenTree (parenTree) import Bench.Vector.TestData.Graph (randomGraph) @@ -68,4 +70,14 @@ main = do , bench "minimumOn" $ whnf (U.minimumOn (\x -> x*x*x)) as , bench "maximumOn" $ whnf (U.maximumOn (\x -> x*x*x)) as , bgroup "(next|prev)Permutation" $ map (\(name, act) -> bench name $ whnfIO act) permTests + , bgroup "Applicative" + [ bench "generateState" $ whnf generateState useSize + , bench "generateStateUnfold" $ whnf generateStateUnfold useSize + , bench "generateIO" $ whnfIO (generateIO useSize) + , bench "generateIOPrim" $ whnfIO (generateIOPrim useSize) + , bench "sum[lens]" $ whnf lensSum as + , bench "sum[base]" $ whnf baselineSum as + , bench "map[lens]" $ whnf lensMap as + , bench "map[base]" $ whnf baselineMap as + ] ] diff --git a/vector/benchlib/Bench/Vector/Algo/Applicative.hs b/vector/benchlib/Bench/Vector/Algo/Applicative.hs new file mode 100644 index 00000000..06f4cd74 --- /dev/null +++ b/vector/benchlib/Bench/Vector/Algo/Applicative.hs @@ -0,0 +1,101 @@ +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +-- | +-- This module provides benchmarks for functions which use API based +-- on applicative. We use @generateA@ based benchmark for state and IO +-- and also benchmark folds and mapping using lens since it's one of +-- important consumers of this API. +module Bench.Vector.Algo.Applicative + ( -- * Standard benchmarks + generateState + , generateStateUnfold + , generateIO + , generateIOPrim + -- * Lens benchmarks + , lensSum + , baselineSum + , lensMap + , baselineMap + ) where + +import Control.Applicative +import Data.Coerce +import Data.Functor.Identity +import Data.Int +import Data.Monoid +import Data.Word +import qualified Data.Vector.Generic as VG +import qualified Data.Vector.Generic.Mutable as MVG +import qualified Data.Vector.Unboxed as VU +import System.Random.Stateful +import System.Mem (getAllocationCounter) + +-- | Benchmark which is running in state monad. +generateState :: Int -> VU.Vector Word64 +generateState n + = runStateGen_ (mkStdGen 42) + $ \g -> VG.generateA n (\_ -> uniformM g) + +-- | Benchmark which is running in state monad. +generateStateUnfold :: Int -> VU.Vector Word64 +generateStateUnfold n = VU.unfoldrExactN n genWord64 (mkStdGen 42) + +-- | Benchmark for running @generateA@ in IO monad. +generateIO :: Int -> IO (VU.Vector Int64) +generateIO n = VG.generateA n (\_ -> getAllocationCounter) + +-- | Baseline for 'generateIO' it uses primitive operations +generateIOPrim :: Int -> IO (VU.Vector Int64) +generateIOPrim n = VG.unsafeFreeze =<< MVG.replicateM n getAllocationCounter + +-- | Sum using lens +lensSum :: VU.Vector Double -> Double +{-# NOINLINE lensSum #-} +lensSum = foldlOf' VG.traverse (+) 0 + +-- | Baseline for sum. +baselineSum :: VU.Vector Double -> Double +{-# NOINLINE baselineSum #-} +baselineSum = VU.sum + +-- | Mapping over vector elements using +lensMap :: VU.Vector Double -> VU.Vector Double +{-# NOINLINE lensMap #-} +lensMap = over VG.traverse (*2) + +-- | Baseline for map +baselineMap :: VU.Vector Double -> VU.Vector Double +{-# NOINLINE baselineMap #-} +baselineMap = VU.map (*2) + +---------------------------------------------------------------- +-- Bits and pieces of lens +-- +-- We don't want to depend on lens so we just copy relevant +-- parts. After all we don't need much +---------------------------------------------------------------- + +type ASetter s t a b = (a -> Identity b) -> s -> Identity t +type Getting r s a = (a -> Const r a) -> s -> Const r s + +foldlOf' :: Getting (Endo (Endo r)) s a -> (r -> a -> r) -> r -> s -> r +foldlOf' l f z0 = \xs -> + let f' x (Endo k) = Endo $ \z -> k $! f z x + in foldrOf l f' (Endo id) xs `appEndo` z0 +{-# INLINE foldlOf' #-} + +foldrOf :: Getting (Endo r) s a -> (a -> r -> r) -> r -> s -> r +foldrOf l f z = flip appEndo z . foldMapOf l (Endo #. f) +{-# INLINE foldrOf #-} + +foldMapOf :: Getting r s a -> (a -> r) -> s -> r +foldMapOf = coerce +{-# INLINE foldMapOf #-} + +( #. ) :: Coercible c b => (b -> c) -> (a -> b) -> (a -> c) +( #. ) _ = coerce (\x -> x :: b) :: forall a b. Coercible b a => a -> b +{-# INLINE (#.) #-} + +over :: ASetter s t a b -> (a -> b) -> s -> t +over = coerce +{-# INLINE over #-} diff --git a/vector/benchmarks/Main.hs b/vector/benchmarks/Main.hs index f8aad4ea..79adb3b9 100644 --- a/vector/benchmarks/Main.hs +++ b/vector/benchmarks/Main.hs @@ -1,6 +1,7 @@ {-# LANGUAGE BangPatterns #-} module Main where + import Bench.Vector.Algo.MutableSet (mutableSet) import Bench.Vector.Algo.ListRank (listRank) import Bench.Vector.Algo.Rootfix (rootfix) @@ -12,6 +13,8 @@ import Bench.Vector.Algo.Spectral (spectral) import Bench.Vector.Algo.Tridiag (tridiag) import Bench.Vector.Algo.FindIndexR (findIndexR, findIndexR_naive, findIndexR_manual) import Bench.Vector.Algo.NextPermutation (generatePermTests) +import Bench.Vector.Algo.Applicative ( generateState, generateStateUnfold, generateIO, generateIOPrim + , lensSum, lensMap, baselineSum, baselineMap) import Bench.Vector.TestData.ParenTree (parenTree) import Bench.Vector.TestData.Graph (randomGraph) @@ -69,4 +72,14 @@ main = do , bench "minimumOn" $ whnf (U.minimumOn (\x -> x*x*x)) as , bench "maximumOn" $ whnf (U.maximumOn (\x -> x*x*x)) as , bgroup "(next|prev)Permutation" $ map (\(name, act) -> bench name $ whnfIO act) permTests + , bgroup "Applicative" + [ bench "generateState" $ whnf generateState useSize + , bench "generateStateUnfold" $ whnf generateStateUnfold useSize + , bench "generateIO" $ whnfIO (generateIO useSize) + , bench "generateIOPrim" $ whnfIO (generateIOPrim useSize) + , bench "sum[lens]" $ whnf lensSum as + , bench "sum[base]" $ whnf baselineSum as + , bench "map[lens]" $ whnf lensMap as + , bench "map[base]" $ whnf baselineMap as + ] ] diff --git a/vector/changelog.md b/vector/changelog.md index b169f9be..2259a7ff 100644 --- a/vector/changelog.md +++ b/vector/changelog.md @@ -1,3 +1,11 @@ +# Changes in version NEXT_VERSION + + * [#522](https://github.com/haskell/vector/pull/522) API using Applicatives + added: `traverse` & friends. + * [#518](https://github.com/haskell/vector/pull/518) `UnboxViaStorable` added. + Vector constructors are reexported for `DoNotUnbox*`. + * [#531](https://github.com/haskell/vector/pull/531) `iconcatMap` added. + # Changes in version 0.13.2.0 * Strict boxed vector `Data.Vector.Strict` and `Data.Vector.Strict.Mutable` is diff --git a/vector/src/Data/Vector.hs b/vector/src/Data/Vector.hs index 8ea3ca4d..cc7112d9 100644 --- a/vector/src/Data/Vector.hs +++ b/vector/src/Data/Vector.hs @@ -156,6 +156,10 @@ module Data.Vector ( scanr, scanr', scanr1, scanr1', iscanr, iscanr', + -- * Applicative API + replicateA, generateA, traverse, itraverse, forA, iforA, + traverse_, itraverse_, forA_, iforA_, + -- ** Comparisons eqBy, cmpBy, @@ -174,6 +178,7 @@ module Data.Vector ( freeze, thaw, copy, unsafeFreeze, unsafeThaw, unsafeCopy ) where +import Control.Applicative (Applicative) import Data.Vector.Mutable ( MVector(..) ) import Data.Primitive.Array import qualified Data.Vector.Fusion.Bundle as Bundle @@ -2205,6 +2210,97 @@ fromListN :: Int -> [a] -> Vector a {-# INLINE fromListN #-} fromListN = G.fromListN +-- Applicative +-- ----------- + +-- | Construct a vector of the given length by applying the applicative +-- action to each index. +-- +-- @since NEXT_VERSION +generateA :: (Applicative f) => Int -> (Int -> f a) -> f (Vector a) +generateA = G.generateA + +-- | Execute the applicative action the given number of times and store the +-- results in a vector. +-- +-- @since NEXT_VERSION +replicateA :: (Applicative f) => Int -> f a -> f (Vector a) +{-# INLINE replicateA #-} +replicateA = G.replicateA + +-- | Apply the applicative action to all elements of the vector, yielding a +-- vector of results. +-- +-- @since NEXT_VERSION +traverse :: (Applicative f) + => (a -> f b) -> Vector a -> f (Vector b) +{-# INLINE traverse #-} +traverse = G.traverse + +-- | Apply the applicative action to every element of a vector and its +-- index, yielding a vector of results. +-- +-- @since NEXT_VERSION +itraverse :: (Applicative f) + => (Int -> a -> f b) -> Vector a -> f (Vector b) +{-# INLINE itraverse #-} +itraverse = G.itraverse + +-- | Apply the applicative action to all elements of the vector, yielding a +-- vector of results. This is flipped version of 'traverse'. +-- +-- @since NEXT_VERSION +forA :: (Applicative f) + => Vector a -> (a -> f b) -> f (Vector b) +{-# INLINE forA #-} +forA = G.forA + +-- | Apply the applicative action to every element of a vector and its +-- index, yielding a vector of results. This is flipped version of 'itraverse'. +-- +-- @since NEXT_VERSION +iforA :: (Applicative f) + => Vector a -> (Int -> a -> f b) -> f (Vector b) +{-# INLINE iforA #-} +iforA = G.iforA + +-- | Map each element of a structure to an 'Applicative' action, evaluate these +-- actions from left to right, and ignore the results. +-- +-- @since NEXT_VERSION +traverse_ :: (Applicative f) + => (a -> f b) -> Vector a -> f () +{-# INLINE traverse_ #-} +traverse_ = G.traverse_ + +-- | Map each element of a structure to an 'Applicative' action, evaluate these +-- actions from left to right, and ignore the results. +-- +-- @since NEXT_VERSION +itraverse_ :: (Applicative f) + => (Int -> a -> f b) -> Vector a -> f () +{-# INLINE itraverse_ #-} +itraverse_ = G.itraverse_ + +-- | Map each element of a structure to an 'Applicative' action, evaluate these +-- actions from left to right, and ignore the results. +-- +-- @since NEXT_VERSION +forA_ :: (Applicative f) + => Vector a -> (a -> f b) -> f () +{-# INLINE forA_ #-} +forA_ = G.forA_ + +-- | Map each element of a structure to an 'Applicative' action, evaluate these +-- actions from left to right, and ignore the results. +-- +-- @since NEXT_VERSION +iforA_ :: (Applicative f) + => Vector a -> (Int -> a -> f b) -> f () +{-# INLINE iforA_ #-} +iforA_ = G.iforA_ + + -- Conversions - Arrays -- ----------------------------- diff --git a/vector/src/Data/Vector/Generic.hs b/vector/src/Data/Vector/Generic.hs index a8250b4d..9afcfc46 100644 --- a/vector/src/Data/Vector/Generic.hs +++ b/vector/src/Data/Vector/Generic.hs @@ -5,6 +5,7 @@ {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeApplications #-} -- | -- Module : Data.Vector.Generic -- Copyright : (c) Roman Leshchinskiy 2008-2010 @@ -145,6 +146,10 @@ module Data.Vector.Generic ( scanr, scanr', scanr1, scanr1', iscanr, iscanr', + -- * Applicative API + replicateA, generateA, traverse, itraverse, forA, iforA, + traverse_, itraverse_, forA_, iforA_, + -- * Conversions -- ** Lists @@ -178,6 +183,7 @@ module Data.Vector.Generic ( gfoldl, gunfold, dataCast, mkVecType, mkVecConstr, mkType ) where +import Control.Applicative (Applicative(..), liftA2) import Data.Vector.Generic.Base import qualified Data.Vector.Generic.Mutable as M @@ -196,11 +202,12 @@ import Data.Vector.Internal.Check import Control.Monad.ST ( ST, runST ) import Control.Monad.Primitive +import Data.Functor.Identity (Identity(..)) import Prelude - ( Eq, Ord, Num, Enum, Monoid, Monad, Read, Show, Bool, Ordering(..), Int, Maybe(..), Either, IO, ShowS, ReadS, String + ( Eq(..), Ord(..), Num, Enum, Monoid, Monad, Read, Show, Bool, Ordering(..) + , Int, Maybe(..), Either, IO, ShowS, ReadS, String , compare, mempty, mappend, return, fmap, otherwise, id, flip, seq, error, undefined, uncurry, shows, fst, snd, min, max, not - , (>>=), (+), (-), (*), (<), (==), (.), ($), (=<<), (>>), (<$>) ) - + , (>>=), (+), (-), (*), (.), ($), (=<<), (>>), (<$>)) import qualified Text.Read as Read import qualified Data.List.NonEmpty as NonEmpty @@ -2653,6 +2660,148 @@ clone v = v `seq` New.create ( unsafeCopy mv v return mv) +-- Applicatives +-- ------------ + + + +newtype STA v a = STA { + _runSTA :: forall s. Mutable v s a -> ST s () +} + +runSTA :: Vector v a => Int -> STA v a -> v a +runSTA !sz = \(STA fun) -> runST $ do + mv <- M.unsafeNew sz + fun mv + unsafeFreeze mv +{-# INLINE runSTA #-} + + +-- | Construct a vector of the given length by applying the applicative +-- action to each index. +-- +-- @since NEXT_VERSION +generateA :: (Applicative f, Vector v a) => Int -> (Int -> f a) -> f (v a) +{-# INLINE[1] generateA #-} +generateA n f + | n <= 0 = pure empty + | otherwise = runSTA n <$> go 0 + where + go !i | i >= n = pure $ STA $ \_ -> pure () + | otherwise = liftA2 + (\a (STA m) -> STA $ \mv -> M.unsafeWrite mv i a >> m mv) + (f i) + (go (i + 1)) + +unsafeGeneratePrim :: (PrimMonad m, Vector v a) => Int -> (Int -> m a) -> m (v a) +{-# INLINE unsafeGeneratePrim #-} +unsafeGeneratePrim n f = unsafeFreeze =<< M.generateM n f + +generateA_IO :: (Vector v a) => Int -> (Int -> IO a) -> IO (v a) +{-# INLINE generateA_IO #-} +generateA_IO = unsafeGeneratePrim + +generateA_ST :: (Vector v a) => Int -> (Int -> ST s a) -> ST s (v a) +{-# INLINE generateA_ST #-} +generateA_ST = unsafeGeneratePrim + +-- Identity is used in lest for mapping over structures. So it's +-- relatively important case. +generateA_Identity :: (Vector v a) => Int -> (Int -> Identity a) -> Identity (v a) +{-# INLINE generateA_Identity #-} +generateA_Identity n f = Identity (generate n (runIdentity . f)) + + +{-# RULES + +"generateA[IO]" generateA = generateA_IO +"generateA[ST]" generateA = generateA_ST +"generateA[Identity]" generateA = generateA_Identity + #-} + + +-- | Execute the applicative action the given number of times and store the +-- results in a vector. +-- +-- @since NEXT_VERSION +replicateA :: (Applicative f, Vector v a) => Int -> f a -> f (v a) +{-# INLINE replicateA #-} +replicateA n f = generateA n (\_ -> f) + +-- | Apply the applicative action to all elements of the vector, yielding a +-- vector of results. +-- +-- @since NEXT_VERSION +traverse :: (Applicative f, Vector v a, Vector v b) + => (a -> f b) -> v a -> f (v b) +{-# INLINE traverse #-} +traverse f v = generateA (length v) $ \i -> f (unsafeIndex v i) + +-- | Apply the applicative action to every element of a vector and its +-- index, yielding a vector of results. +-- +-- @since NEXT_VERSION +itraverse :: (Applicative f, Vector v a, Vector v b) + => (Int -> a -> f b) -> v a -> f (v b) +{-# INLINE itraverse #-} +itraverse f v = generateA (length v) $ \i -> f i (unsafeIndex v i) + +-- | Apply the applicative action to all elements of the vector, yielding a +-- vector of results. This is flipped version of 'traverse'. +-- +-- @since NEXT_VERSION +forA :: (Applicative f, Vector v a, Vector v b) + => v a -> (a -> f b) -> f (v b) +{-# INLINE forA #-} +forA v f = generateA (length v) $ \i -> f (unsafeIndex v i) + +-- | Apply the applicative action to every element of a vector and its +-- index, yielding a vector of results. This is flipped version of 'itraverse'. +-- +-- @since NEXT_VERSION +iforA :: (Applicative f, Vector v a, Vector v b) + => v a -> (Int -> a -> f b) -> f (v b) +{-# INLINE iforA #-} +iforA v f = generateA (length v) $ \i -> f i (unsafeIndex v i) + +-- | Map each element of a structure to an 'Applicative' action, evaluate these +-- actions from left to right, and ignore the results. +-- +-- @since NEXT_VERSION +traverse_ :: (Applicative f, Vector v a) + => (a -> f b) -> v a -> f () +{-# INLINE traverse_ #-} +traverse_ f = foldr step (pure ()) + where step x k = f x *> k + +-- | Map each element of a structure to an 'Applicative' action, evaluate these +-- actions from left to right, and ignore the results. +-- +-- @since NEXT_VERSION +itraverse_ :: (Applicative f, Vector v a) + => (Int -> a -> f b) -> v a -> f () +{-# INLINE itraverse_ #-} +itraverse_ f = ifoldr step (pure ()) + where step i x k = f i x *> k + +-- | Map each element of a structure to an 'Applicative' action, evaluate these +-- actions from left to right, and ignore the results. +-- +-- @since NEXT_VERSION +forA_ :: (Applicative f, Vector v a) + => v a -> (a -> f b) -> f () +{-# INLINE forA_ #-} +forA_ = flip traverse_ + +-- | Map each element of a structure to an 'Applicative' action, evaluate these +-- actions from left to right, and ignore the results. +-- +-- @since NEXT_VERSION +iforA_ :: (Applicative f, Vector v a) + => v a -> (Int -> a -> f b) -> f () +{-# INLINE iforA_ #-} +iforA_ = flip itraverse_ + -- Comparisons -- ----------- diff --git a/vector/src/Data/Vector/Primitive.hs b/vector/src/Data/Vector/Primitive.hs index 7e23ea61..a0b81680 100644 --- a/vector/src/Data/Vector/Primitive.hs +++ b/vector/src/Data/Vector/Primitive.hs @@ -138,6 +138,10 @@ module Data.Vector.Primitive ( scanr, scanr', scanr1, scanr1', iscanr, iscanr', + -- * Applicative API + replicateA, generateA, traverse, itraverse, forA, iforA, + traverse_, itraverse_, forA_, iforA_, + -- ** Comparisons eqBy, cmpBy, @@ -157,6 +161,7 @@ module Data.Vector.Primitive ( Prim ) where +import Control.Applicative (Applicative) import qualified Data.Vector.Generic as G import Data.Vector.Primitive.Mutable ( MVector(..) ) import Data.Vector.Internal.Check @@ -1875,6 +1880,98 @@ fromListN :: Prim a => Int -> [a] -> Vector a {-# INLINE fromListN #-} fromListN = G.fromListN + +-- Applicative +-- ----------- + +-- | Construct a vector of the given length by applying the applicative +-- action to each index. +-- +-- @since NEXT_VERSION +generateA :: (Applicative f, Prim a) => Int -> (Int -> f a) -> f (Vector a) +generateA = G.generateA + +-- | Execute the applicative action the given number of times and store the +-- results in a vector. +-- +-- @since NEXT_VERSION +replicateA :: (Applicative f, Prim a) => Int -> f a -> f (Vector a) +{-# INLINE replicateA #-} +replicateA = G.replicateA + +-- | Apply the applicative action to all elements of the vector, yielding a +-- vector of results. +-- +-- @since NEXT_VERSION +traverse :: (Applicative f, Prim a, Prim b) + => (a -> f b) -> Vector a -> f (Vector b) +{-# INLINE traverse #-} +traverse = G.traverse + +-- | Apply the applicative action to every element of a vector and its +-- index, yielding a vector of results. +-- +-- @since NEXT_VERSION +itraverse :: (Applicative f, Prim a, Prim b) + => (Int -> a -> f b) -> Vector a -> f (Vector b) +{-# INLINE itraverse #-} +itraverse = G.itraverse + +-- | Apply the applicative action to all elements of the vector, yielding a +-- vector of results. This is flipped version of 'traverse'. +-- +-- @since NEXT_VERSION +forA :: (Applicative f, Prim a, Prim b) + => Vector a -> (a -> f b) -> f (Vector b) +{-# INLINE forA #-} +forA = G.forA + +-- | Apply the applicative action to every element of a vector and its +-- index, yielding a vector of results. This is flipped version of 'itraverse'. +-- +-- @since NEXT_VERSION +iforA :: (Applicative f, Prim a, Prim b) + => Vector a -> (Int -> a -> f b) -> f (Vector b) +{-# INLINE iforA #-} +iforA = G.iforA + +-- | Map each element of a structure to an 'Applicative' action, evaluate these +-- actions from left to right, and ignore the results. +-- +-- @since NEXT_VERSION +traverse_ :: (Applicative f, Prim a) + => (a -> f b) -> Vector a -> f () +{-# INLINE traverse_ #-} +traverse_ = G.traverse_ + +-- | Map each element of a structure to an 'Applicative' action, evaluate these +-- actions from left to right, and ignore the results. +-- +-- @since NEXT_VERSION +itraverse_ :: (Applicative f, Prim a) + => (Int -> a -> f b) -> Vector a -> f () +{-# INLINE itraverse_ #-} +itraverse_ = G.itraverse_ + +-- | Map each element of a structure to an 'Applicative' action, evaluate these +-- actions from left to right, and ignore the results. +-- +-- @since NEXT_VERSION +forA_ :: (Applicative f, Prim a) + => Vector a -> (a -> f b) -> f () +{-# INLINE forA_ #-} +forA_ = G.forA_ + +-- | Map each element of a structure to an 'Applicative' action, evaluate these +-- actions from left to right, and ignore the results. +-- +-- @since NEXT_VERSION +iforA_ :: (Applicative f, Prim a) + => Vector a -> (Int -> a -> f b) -> f () +{-# INLINE iforA_ #-} +iforA_ = G.iforA_ + + -- Conversions - Unsafe casts -- -------------------------- diff --git a/vector/src/Data/Vector/Storable.hs b/vector/src/Data/Vector/Storable.hs index 65565b05..597c8ad7 100644 --- a/vector/src/Data/Vector/Storable.hs +++ b/vector/src/Data/Vector/Storable.hs @@ -135,6 +135,10 @@ module Data.Vector.Storable ( scanr, scanr', scanr1, scanr1', iscanr, iscanr', + -- * Applicative API + replicateA, generateA, traverse, itraverse, forA, iforA, + traverse_, itraverse_, forA_, iforA_, + -- ** Comparisons eqBy, cmpBy, @@ -163,6 +167,7 @@ module Data.Vector.Storable ( Storable ) where +import Control.Applicative (Applicative) import qualified Data.Vector.Generic as G import Data.Vector.Storable.Mutable ( MVector(..) ) import Data.Vector.Storable.Internal @@ -1921,6 +1926,98 @@ fromListN :: Storable a => Int -> [a] -> Vector a {-# INLINE fromListN #-} fromListN = G.fromListN + +-- Applicative +-- ----------- + +-- | Construct a vector of the given length by applying the applicative +-- action to each index. +-- +-- @since NEXT_VERSION +generateA :: (Applicative f, Storable a) => Int -> (Int -> f a) -> f (Vector a) +generateA = G.generateA + +-- | Execute the applicative action the given number of times and store the +-- results in a vector. +-- +-- @since NEXT_VERSION +replicateA :: (Applicative f, Storable a) => Int -> f a -> f (Vector a) +{-# INLINE replicateA #-} +replicateA = G.replicateA + +-- | Apply the applicative action to all elements of the vector, yielding a +-- vector of results. +-- +-- @since NEXT_VERSION +traverse :: (Applicative f, Storable a, Storable b) + => (a -> f b) -> Vector a -> f (Vector b) +{-# INLINE traverse #-} +traverse = G.traverse + +-- | Apply the applicative action to every element of a vector and its +-- index, yielding a vector of results. +-- +-- @since NEXT_VERSION +itraverse :: (Applicative f, Storable a, Storable b) + => (Int -> a -> f b) -> Vector a -> f (Vector b) +{-# INLINE itraverse #-} +itraverse = G.itraverse + +-- | Apply the applicative action to all elements of the vector, yielding a +-- vector of results. This is flipped version of 'traverse'. +-- +-- @since NEXT_VERSION +forA :: (Applicative f, Storable a, Storable b) + => Vector a -> (a -> f b) -> f (Vector b) +{-# INLINE forA #-} +forA = G.forA + +-- | Apply the applicative action to every element of a vector and its +-- index, yielding a vector of results. This is flipped version of 'itraverse'. +-- +-- @since NEXT_VERSION +iforA :: (Applicative f, Storable a, Storable b) + => Vector a -> (Int -> a -> f b) -> f (Vector b) +{-# INLINE iforA #-} +iforA = G.iforA + +-- | Map each element of a structure to an 'Applicative' action, evaluate these +-- actions from left to right, and ignore the results. +-- +-- @since NEXT_VERSION +traverse_ :: (Applicative f, Storable a) + => (a -> f b) -> Vector a -> f () +{-# INLINE traverse_ #-} +traverse_ = G.traverse_ + +-- | Map each element of a structure to an 'Applicative' action, evaluate these +-- actions from left to right, and ignore the results. +-- +-- @since NEXT_VERSION +itraverse_ :: (Applicative f, Storable a) + => (Int -> a -> f b) -> Vector a -> f () +{-# INLINE itraverse_ #-} +itraverse_ = G.itraverse_ + +-- | Map each element of a structure to an 'Applicative' action, evaluate these +-- actions from left to right, and ignore the results. +-- +-- @since NEXT_VERSION +forA_ :: (Applicative f, Storable a) + => Vector a -> (a -> f b) -> f () +{-# INLINE forA_ #-} +forA_ = G.forA_ + +-- | Map each element of a structure to an 'Applicative' action, evaluate these +-- actions from left to right, and ignore the results. +-- +-- @since NEXT_VERSION +iforA_ :: (Applicative f, Storable a) + => Vector a -> (Int -> a -> f b) -> f () +{-# INLINE iforA_ #-} +iforA_ = G.iforA_ + + -- Conversions - Unsafe casts -- -------------------------- diff --git a/vector/src/Data/Vector/Strict.hs b/vector/src/Data/Vector/Strict.hs index c6c3eb3a..ece68780 100644 --- a/vector/src/Data/Vector/Strict.hs +++ b/vector/src/Data/Vector/Strict.hs @@ -154,6 +154,10 @@ module Data.Vector.Strict ( scanr, scanr', scanr1, scanr1', iscanr, iscanr', + -- * Applicative API + replicateA, generateA, traverse, itraverse, forA, iforA, + traverse_, itraverse_, forA_, iforA_, + -- ** Comparisons eqBy, cmpBy, @@ -173,6 +177,7 @@ module Data.Vector.Strict ( freeze, thaw, copy, unsafeFreeze, unsafeThaw, unsafeCopy ) where +import Control.Applicative (Applicative) import Data.Coerce import Data.Vector.Strict.Mutable ( MVector(..) ) import Data.Primitive.Array @@ -2477,6 +2482,97 @@ fromListN :: Int -> [a] -> Vector a {-# INLINE fromListN #-} fromListN = G.fromListN +-- Applicative +-- ----------- + +-- | Construct a vector of the given length by applying the applicative +-- action to each index. +-- +-- @since NEXT_VERSION +generateA :: (Applicative f) => Int -> (Int -> f a) -> f (Vector a) +generateA = G.generateA + +-- | Execute the applicative action the given number of times and store the +-- results in a vector. +-- +-- @since NEXT_VERSION +replicateA :: (Applicative f) => Int -> f a -> f (Vector a) +{-# INLINE replicateA #-} +replicateA = G.replicateA + +-- | Apply the applicative action to all elements of the vector, yielding a +-- vector of results. +-- +-- @since NEXT_VERSION +traverse :: (Applicative f) + => (a -> f b) -> Vector a -> f (Vector b) +{-# INLINE traverse #-} +traverse = G.traverse + +-- | Apply the applicative action to every element of a vector and its +-- index, yielding a vector of results. +-- +-- @since NEXT_VERSION +itraverse :: (Applicative f) + => (Int -> a -> f b) -> Vector a -> f (Vector b) +{-# INLINE itraverse #-} +itraverse = G.itraverse + +-- | Apply the applicative action to all elements of the vector, yielding a +-- vector of results. This is flipped version of 'traverse'. +-- +-- @since NEXT_VERSION +forA :: (Applicative f) + => Vector a -> (a -> f b) -> f (Vector b) +{-# INLINE forA #-} +forA = G.forA + +-- | Apply the applicative action to every element of a vector and its +-- index, yielding a vector of results. This is flipped version of 'itraverse'. +-- +-- @since NEXT_VERSION +iforA :: (Applicative f) + => Vector a -> (Int -> a -> f b) -> f (Vector b) +{-# INLINE iforA #-} +iforA = G.iforA + +-- | Map each element of a structure to an 'Applicative' action, evaluate these +-- actions from left to right, and ignore the results. +-- +-- @since NEXT_VERSION +traverse_ :: (Applicative f) + => (a -> f b) -> Vector a -> f () +{-# INLINE traverse_ #-} +traverse_ = G.traverse_ + +-- | Map each element of a structure to an 'Applicative' action, evaluate these +-- actions from left to right, and ignore the results. +-- +-- @since NEXT_VERSION +itraverse_ :: (Applicative f) + => (Int -> a -> f b) -> Vector a -> f () +{-# INLINE itraverse_ #-} +itraverse_ = G.itraverse_ + +-- | Map each element of a structure to an 'Applicative' action, evaluate these +-- actions from left to right, and ignore the results. +-- +-- @since NEXT_VERSION +forA_ :: (Applicative f) + => Vector a -> (a -> f b) -> f () +{-# INLINE forA_ #-} +forA_ = G.forA_ + +-- | Map each element of a structure to an 'Applicative' action, evaluate these +-- actions from left to right, and ignore the results. +-- +-- @since NEXT_VERSION +iforA_ :: (Applicative f) + => Vector a -> (Int -> a -> f b) -> f () +{-# INLINE iforA_ #-} +iforA_ = G.iforA_ + + -- Conversions - Lazy vectors -- ----------------------------- diff --git a/vector/src/Data/Vector/Unboxed.hs b/vector/src/Data/Vector/Unboxed.hs index 57ee1118..a1f356bd 100644 --- a/vector/src/Data/Vector/Unboxed.hs +++ b/vector/src/Data/Vector/Unboxed.hs @@ -193,6 +193,10 @@ module Data.Vector.Unboxed ( scanr, scanr', scanr1, scanr1', iscanr, iscanr', + -- * Applicative API + replicateA, generateA, traverse, itraverse, forA, iforA, + traverse_, itraverse_, forA_, iforA_, + -- ** Comparisons eqBy, cmpBy, @@ -221,6 +225,7 @@ module Data.Vector.Unboxed ( DoNotUnboxNormalForm(..) ) where +import Control.Applicative (Applicative) import Data.Vector.Unboxed.Base import qualified Data.Vector.Generic as G import qualified Data.Vector.Fusion.Bundle as Bundle @@ -2012,6 +2017,98 @@ fromListN :: Unbox a => Int -> [a] -> Vector a {-# INLINE fromListN #-} fromListN = G.fromListN + +-- Applicative +-- ----------- + +-- | Construct a vector of the given length by applying the applicative +-- action to each index. +-- +-- @since NEXT_VERSION +generateA :: (Applicative f, Unbox a) => Int -> (Int -> f a) -> f (Vector a) +generateA = G.generateA + +-- | Execute the applicative action the given number of times and store the +-- results in a vector. +-- +-- @since NEXT_VERSION +replicateA :: (Applicative f, Unbox a) => Int -> f a -> f (Vector a) +{-# INLINE replicateA #-} +replicateA = G.replicateA + +-- | Apply the applicative action to all elements of the vector, yielding a +-- vector of results. +-- +-- @since NEXT_VERSION +traverse :: (Applicative f, Unbox a, Unbox b) + => (a -> f b) -> Vector a -> f (Vector b) +{-# INLINE traverse #-} +traverse = G.traverse + +-- | Apply the applicative action to every element of a vector and its +-- index, yielding a vector of results. +-- +-- @since NEXT_VERSION +itraverse :: (Applicative f, Unbox a, Unbox b) + => (Int -> a -> f b) -> Vector a -> f (Vector b) +{-# INLINE itraverse #-} +itraverse = G.itraverse + +-- | Apply the applicative action to all elements of the vector, yielding a +-- vector of results. This is flipped version of 'traverse'. +-- +-- @since NEXT_VERSION +forA :: (Applicative f, Unbox a, Unbox b) + => Vector a -> (a -> f b) -> f (Vector b) +{-# INLINE forA #-} +forA = G.forA + +-- | Apply the applicative action to every element of a vector and its +-- index, yielding a vector of results. This is flipped version of 'itraverse'. +-- +-- @since NEXT_VERSION +iforA :: (Applicative f, Unbox a, Unbox b) + => Vector a -> (Int -> a -> f b) -> f (Vector b) +{-# INLINE iforA #-} +iforA = G.iforA + +-- | Map each element of a structure to an 'Applicative' action, evaluate these +-- actions from left to right, and ignore the results. +-- +-- @since NEXT_VERSION +traverse_ :: (Applicative f, Unbox a) + => (a -> f b) -> Vector a -> f () +{-# INLINE traverse_ #-} +traverse_ = G.traverse_ + +-- | Map each element of a structure to an 'Applicative' action, evaluate these +-- actions from left to right, and ignore the results. +-- +-- @since NEXT_VERSION +itraverse_ :: (Applicative f, Unbox a) + => (Int -> a -> f b) -> Vector a -> f () +{-# INLINE itraverse_ #-} +itraverse_ = G.itraverse_ + +-- | Map each element of a structure to an 'Applicative' action, evaluate these +-- actions from left to right, and ignore the results. +-- +-- @since NEXT_VERSION +forA_ :: (Applicative f, Unbox a) + => Vector a -> (a -> f b) -> f () +{-# INLINE forA_ #-} +forA_ = G.forA_ + +-- | Map each element of a structure to an 'Applicative' action, evaluate these +-- actions from left to right, and ignore the results. +-- +-- @since NEXT_VERSION +iforA_ :: (Applicative f, Unbox a) + => Vector a -> (Int -> a -> f b) -> f () +{-# INLINE iforA_ #-} +iforA_ = G.iforA_ + + -- Conversions - Mutable vectors -- ----------------------------- diff --git a/vector/tests/Tests/Vector/Boxed.hs b/vector/tests/Tests/Vector/Boxed.hs index 2acbaa1c..b8c85e90 100644 --- a/vector/tests/Tests/Vector/Boxed.hs +++ b/vector/tests/Tests/Vector/Boxed.hs @@ -22,6 +22,7 @@ testGeneralBoxedVector dummy = concatMap ($ dummy) , testFunctorFunctions , testMonadFunctions , testApplicativeFunctions + , testTraverseFunctions , testAlternativeFunctions , testSequenceFunctions , testDataFunctions diff --git a/vector/tests/Tests/Vector/Primitive.hs b/vector/tests/Tests/Vector/Primitive.hs index 75592052..9e5e200a 100644 --- a/vector/tests/Tests/Vector/Primitive.hs +++ b/vector/tests/Tests/Vector/Primitive.hs @@ -17,6 +17,7 @@ testGeneralPrimitiveVector dummy = concatMap ($ dummy) , inline testPolymorphicFunctions , testOrdFunctions , testMonoidFunctions + , testTraverseFunctions , testDataFunctions ] diff --git a/vector/tests/Tests/Vector/Property.hs b/vector/tests/Tests/Vector/Property.hs index 135818d7..f1326850 100644 --- a/vector/tests/Tests/Vector/Property.hs +++ b/vector/tests/Tests/Vector/Property.hs @@ -15,6 +15,7 @@ module Tests.Vector.Property , testApplicativeFunctions , testAlternativeFunctions , testSequenceFunctions + , testTraverseFunctions , testBoolFunctions , testNumFunctions , testNestedVectorFunctions @@ -33,7 +34,7 @@ import Control.Monad.ST import qualified Data.Traversable as T (Traversable(..)) import Data.Orphans () import Data.Maybe -import Data.Foldable (foldrM) +import Data.Foldable (foldrM, traverse_, for_) import qualified Data.Vector.Generic as V import qualified Data.Vector.Generic.Mutable as MV import qualified Data.Vector.Fusion.Bundle as S @@ -768,6 +769,40 @@ testApplicativeFunctions _ = $(testProperties prop_applicative_appl :: [a -> a] -> P (v a -> v a) = \fs -> (Applicative.<*>) (V.fromList fs) `eq` (Applicative.<*>) fs +testTraverseFunctions + :: forall a v. ( CommonContext a v + , V.Vector v a + ) + => v a -> [TestTree] +{-# INLINE testTraverseFunctions #-} +testTraverseFunctions _ = $(testProperties + [ 'prop_generateA, 'prop_replicateA, 'prop_traverse, 'prop_itraverse, 'prop_for, 'prop_ifor, + 'prop_traverse_, 'prop_forA_, 'prop_itraverse_, 'prop_iforA_ + ]) + where + prop_traverse :: P ((a -> Writer [a] a) -> v a -> Writer [a] (v a)) + = V.traverse `eq` traverse + prop_itraverse :: P ((Int -> a -> Writer [a] a) -> v a -> Writer [a] (v a)) + = V.itraverse `eq` itraverse + prop_for :: P (v a -> (a -> Writer [a] a) -> Writer [a] (v a)) + = V.forA `eq` flip traverse + prop_ifor :: P (v a -> (Int -> a -> Writer [a] a) -> Writer [a] (v a)) + = V.iforA `eq` flip itraverse + prop_generateA :: P (Int -> (Int -> Writer [a] a) -> Writer [a] (v a)) + = (\n _ -> n < 1000) ===> V.generateA `eq` Util.generateM + prop_replicateA :: P (Int -> (Writer [a] a) -> Writer [a] (v a)) + = (\n _ -> n < 1000) ===> V.replicateA `eq` replicateM + prop_traverse_ :: P ((a -> Writer [a] a) -> v a -> Writer [a] ()) + = V.traverse_ `eq` traverse_ + prop_forA_ :: P (v a -> (a -> Writer [a] a) -> Writer [a] ()) + = V.forA_ `eq` for_ + prop_itraverse_ :: P ((Int -> a -> Writer [a] a) -> v a -> Writer [a] ()) + = V.itraverse_ `eq` itraverse_ + prop_iforA_ :: P (v a -> (Int -> a -> Writer [a] a) -> Writer [a] ()) + = V.iforA_ `eq` flip itraverse_ + + + testAlternativeFunctions :: forall a v. (CommonContext a v, Applicative.Alternative v) => v a -> [TestTree] {-# INLINE testAlternativeFunctions #-} testAlternativeFunctions _ = $(testProperties diff --git a/vector/tests/Tests/Vector/Storable.hs b/vector/tests/Tests/Vector/Storable.hs index 306526eb..5116c870 100644 --- a/vector/tests/Tests/Vector/Storable.hs +++ b/vector/tests/Tests/Vector/Storable.hs @@ -17,6 +17,7 @@ testGeneralStorableVector dummy = concatMap ($ dummy) , inline testPolymorphicFunctions , testOrdFunctions , testMonoidFunctions + , testTraverseFunctions , testDataFunctions ] diff --git a/vector/tests/Tests/Vector/Strict.hs b/vector/tests/Tests/Vector/Strict.hs index b041f5ae..9ad21079 100644 --- a/vector/tests/Tests/Vector/Strict.hs +++ b/vector/tests/Tests/Vector/Strict.hs @@ -22,6 +22,7 @@ testGeneralBoxedVector dummy = concatMap ($ dummy) , testFunctorFunctions , testMonadFunctions , testApplicativeFunctions + , testTraverseFunctions , testAlternativeFunctions , testSequenceFunctions , testDataFunctions diff --git a/vector/tests/Tests/Vector/Unboxed.hs b/vector/tests/Tests/Vector/Unboxed.hs index 9311b422..9fcadd86 100644 --- a/vector/tests/Tests/Vector/Unboxed.hs +++ b/vector/tests/Tests/Vector/Unboxed.hs @@ -17,6 +17,7 @@ testGeneralUnboxedVector dummy = concatMap ($ dummy) , testOrdFunctions , testTuplyFunctions , testMonoidFunctions + , testTraverseFunctions , testDataFunctions ] diff --git a/vector/tests/Utilities.hs b/vector/tests/Utilities.hs index 77e2be86..3c88926c 100644 --- a/vector/tests/Utilities.hs +++ b/vector/tests/Utilities.hs @@ -266,7 +266,11 @@ xs // ps = go xs ps' 0 go [] _ _ = [] -withIndexFirst m f = m (uncurry f) . zip [0..] +-- withIndexFirst :: (Int -> a -> [a]) -> [a] -> [a] + +withIndexFirst :: (((Int, a) -> b) -> [(Int, a)] -> c) + -> ((Int -> a -> b) -> [a] -> c) +withIndexFirst m f = m (uncurry f) . zip [0::Int ..] modifyList :: [a] -> (a -> a) -> Int -> [a] modifyList xs f i = zipWith merge xs (replicate i Nothing ++ [Just f] ++ repeat Nothing) @@ -286,6 +290,12 @@ imapM = withIndexFirst mapM imapM_ :: Monad m => (Int -> a -> m b) -> [a] -> m () imapM_ = withIndexFirst mapM_ +itraverse :: Applicative m => (Int -> a -> m a) -> [a] -> m [a] +itraverse = withIndexFirst traverse + +itraverse_ :: Applicative m => (Int -> a -> m a) -> [a] -> m () +itraverse_ = withIndexFirst traverse_ + iconcatMap :: (Int -> a -> [a]) -> [a] -> [a] iconcatMap f = concat . withIndexFirst map f diff --git a/vector/vector.cabal b/vector/vector.cabal index bb74c9b0..177dac34 100644 --- a/vector/vector.cabal +++ b/vector/vector.cabal @@ -289,6 +289,7 @@ library benchmarks-O2 Bench.Vector.Algo.Quickhull Bench.Vector.Algo.Spectral Bench.Vector.Algo.Tridiag + Bench.Vector.Algo.Applicative Bench.Vector.Algo.FindIndexR Bench.Vector.Algo.NextPermutation Bench.Vector.TestData.ParenTree