-- | This module provides lenses and traversals for working with generic -- vectors. module Data.Vector.Generic.Optics ( toVectorOf -- * Isomorphisms , forced , vector , asStream , asStreamR , cloned , converted -- * Lenses , sliced -- * Traversal of individual indices , ordinals , vectorIx , vectorTraverse ) where import Data.Vector.Fusion.Bundle (Bundle) import Data.Vector.Generic as V hiding (zip, filter, indexed) import Data.Vector.Generic.New (New) import Prelude hiding ((++), length, null, head, tail, init, last, map, reverse) import Optics.Core import Optics.Extra.Internal.Vector import Optics.Internal.Fold import Optics.Internal.IxFold import Optics.Internal.Profunctor import Optics.Internal.Optic -- | @sliced i n@ provides a 'Lens' that edits the @n@ elements starting at -- index @i@ from a 'Lens'. -- -- This is only a valid 'Lens' if you do not change the length of the resulting -- 'Vector'. -- -- Attempting to return a longer or shorter vector will result in violations of -- the 'Lens' laws. -- -- >>> Vector.fromList [1..10] ^. sliced 2 5 == Vector.fromList [3,4,5,6,7] -- True -- -- >>> (Vector.fromList [1..10] & sliced 2 5 % mapped .~ 0) == Vector.fromList [1,2,0,0,0,0,0,8,9,10] -- True sliced :: Vector v a => Int -- ^ @i@ starting index -> Int -- ^ @n@ length -> Lens' (v a) (v a) sliced i n = lensVL $ \f v -> (\v0 -> v // zip [i..i+n-1] (V.toList v0)) <$> f (slice i n v) {-# INLINE sliced #-} -- | Similar to 'toListOf', but returning a 'Vector'. -- -- >>> (toVectorOf each (8,15) :: Vector.Vector Int) == Vector.fromList [8,15] -- True toVectorOf :: (Is k A_Fold, Vector v a) => Optic' k is s a -> s -> v a toVectorOf l s = fromList (toListOf l s) {-# INLINE toVectorOf #-} -- | Convert a list to a 'Vector' (or back.) -- -- >>> ([1,2,3] ^. vector :: Vector.Vector Int) == Vector.fromList [1,2,3] -- True -- -- >>> Vector.fromList [0,8,15] ^. re vector -- [0,8,15] vector :: (Vector v a, Vector v b) => Iso [a] [b] (v a) (v b) vector = iso fromList V.toList {-# INLINE vector #-} -- | Convert a 'Vector' to a finite 'Bundle' (or back.) asStream :: (Vector v a, Vector v b) => Iso (v a) (v b) (Bundle v a) (Bundle v b) asStream = iso stream unstream {-# INLINE asStream #-} -- | Convert a 'Vector' to a finite 'Bundle' from right to left (or back.) asStreamR :: (Vector v a, Vector v b) => Iso (v a) (v b) (Bundle v a) (Bundle v b) asStreamR = iso streamR unstreamR {-# INLINE asStreamR #-} -- | Convert a 'Vector' back and forth to an initializer that when run produces -- a copy of the 'Vector'. cloned :: Vector v a => Iso' (v a) (New v a) cloned = iso clone new {-# INLINE cloned #-} -- | Convert a 'Vector' to a version that doesn't retain any extra memory. forced :: (Vector v a, Vector v b) => Iso (v a) (v b) (v a) (v b) forced = iso force force {-# INLINE forced #-} -- | This 'Traversal' will ignore any duplicates in the supplied list of -- indices. -- -- >>> toListOf (ordinals [1,3,2,5,9,10]) $ Vector.fromList [2,4..40] -- [4,8,6,12,20,22] ordinals :: forall v a. Vector v a => [Int] -> IxTraversal' Int (v a) a ordinals is = itraversalVL $ \f v -> (v //) <$> traverse (\i -> (,) i <$> f i (v ! i)) (ordinalNub (length v) is) {-# INLINE ordinals #-} -- | Like 'ix' but polymorphic in the vector type. vectorIx :: V.Vector v a => Int -> Traversal' (v a) a vectorIx i = traversalVL $ \f v -> if 0 <= i && i < V.length v then (\a -> v V.// [(i, a)]) <$> f (v V.! i) else pure v {-# INLINE vectorIx #-} -- | Indexed vector traversal for a generic vector. vectorTraverse :: forall v w a b. (V.Vector v a, V.Vector w b) => IxTraversal Int (v a) (w b) a b vectorTraverse = Optic vectorTraverse__ {-# INLINE vectorTraverse #-} -- | Different vector implementations are isomorphic to each other. converted :: (Vector v a, Vector w a, Vector v b, Vector w b) => Iso (v a) (v b) (w a) (w b) converted = iso convert convert {-# INLINE converted #-} ---------------------------------------- -- Internal implementations vectorTraverse__ :: (Traversing p, V.Vector v a, V.Vector w b) => Optic__ p j (Int -> j) (v a) (w b) a b vectorTraverse__ = conjoined__ vectorTraverseNoIx__ vectorTraverseIx__ {-# INLINE [0] vectorTraverse__ #-} vectorTraverseNoIx__ :: (Traversing p, V.Vector v a, V.Vector w b) => Optic__ p j j (v a) (w b) a b vectorTraverseNoIx__ = wander $ \f v -> let !n = V.length v in V.fromListN n <$> traverse f (V.toList v) {-# INLINE vectorTraverseNoIx__ #-} vectorTraverseIx__ :: (Traversing p, V.Vector v a, V.Vector w b) => Optic__ p j (Int -> j) (v a) (w b) a b vectorTraverseIx__ = iwander $ \f v -> let !n = V.length v in V.fromListN n <$> itraverse f (V.toList v) {-# INLINE vectorTraverseIx__ #-} -- Because vectorTraverse__ inlines late, GHC needs rewrite rules for all cases -- in order to generate optimal code for each of them. The ones that rewrite -- traversal into a traversal correspond to an early inline. {-# RULES "vectorTraverse__ -> traversed" forall (o :: Star g j a b). vectorTraverse__ o = vectorTraverseNoIx__ (reStar o) :: (V.Vector v a, V.Vector w b) => Star g (Int -> j) (v a) (w b) "vectorTraverse__ -> folded" forall (o :: Forget r j a b). vectorTraverse__ o = foldring__ V.foldr (reForget o) :: (V.Vector v a, V.Vector v b) => Forget r (Int -> j) (v a) (v b) "vectorTraverse__ -> mapped" forall (o :: FunArrow j a b). vectorTraverse__ o = roam V.map (reFunArrow o) :: (V.Vector v a, V.Vector v b) => FunArrow (Int -> j) (v a) (v b) "vectorTraverse__ -> itraversed" forall (o :: IxStar g j a b). vectorTraverse__ o = vectorTraverseIx__ o :: (V.Vector v a, V.Vector w b) => IxStar g (Int -> j) (v a) (w b) "vectorTraverse__ -> ifolded" forall (o :: IxForget r j a b). vectorTraverse__ o = ifoldring__ V.ifoldr o :: (V.Vector v a, V.Vector v b) => IxForget r (Int -> j) (v a) (v b) "vectorTraverse__ -> imapped" forall (o :: IxFunArrow j a b). vectorTraverse__ o = iroam V.imap o :: (V.Vector v a, V.Vector v b) => IxFunArrow (Int -> j) (v a) (v b) #-} -- $setup -- >>> import qualified Data.Vector as Vector