{-# LANGUAGE BangPatterns #-} {-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE EmptyCase #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} -- | Lazy length-indexed list: 'Vec'. module Data.Vec.Lazy ( Vec (..), -- * Construction empty, singleton, withDict, -- * Conversions toPull, fromPull, _Pull, toList, fromList, _Vec, fromListPrefix, reifyList, -- * Indexing (!), ix, _Cons, _head, _tail, cons, head, tail, -- * Concatenation and splitting (++), split, concatMap, concat, chunks, -- * Folds foldMap, foldMap1, ifoldMap, ifoldMap1, foldr, ifoldr, foldl', -- * Special folds length, null, sum, product, -- * Mapping map, imap, traverse, traverse1, itraverse, itraverse_, -- * Zipping zipWith, izipWith, -- * Monadic bind, join, -- * Universe universe, -- * VecEach VecEach (..), ) where import Prelude () import Prelude.Compat (Bool (..), Eq (..), Functor (..), Int, Maybe (..), Monad (..), Monoid (..), Num (..), Ord (..), Show (..), id, seq, showParen, showString, ($), (.), (<$>)) import Control.Applicative (Applicative (..)) import Control.DeepSeq (NFData (..)) import Control.Lens ((<&>)) import Data.Boring (Boring (..)) import Data.Distributive (Distributive (..)) import Data.Fin (Fin) import Data.Functor.Apply (Apply (..)) import Data.Functor.Rep (Representable (..), distributeRep) import Data.Hashable (Hashable (..)) import Data.Nat import Data.Semigroup (Semigroup (..)) import Data.Typeable (Typeable) --- Instances import qualified Control.Lens as I import qualified Data.Foldable as I (Foldable (..)) import qualified Data.Functor.Bind as I (Bind (..)) import qualified Data.Semigroup.Foldable as I (Foldable1 (..)) import qualified Data.Semigroup.Traversable as I (Traversable1 (..)) import qualified Data.Traversable as I (Traversable (..)) import qualified Data.Fin as F import qualified Data.Type.Nat as N import qualified Data.Vec.Pull as P infixr 5 ::: -- | Vector, i.e. length-indexed list. data Vec (n :: Nat) a where VNil :: Vec 'Z a (:::) :: a -> Vec n a -> Vec ('S n) a deriving (Typeable) ------------------------------------------------------------------------------- -- Instances ------------------------------------------------------------------------------- deriving instance Eq a => Eq (Vec n a) deriving instance Ord a => Ord (Vec n a) instance Show a => Show (Vec n a) where showsPrec _ VNil = showString "VNil" showsPrec d (x ::: xs) = showParen (d > 5) $ showsPrec 6 x . showString " ::: " . showsPrec 5 xs instance Functor (Vec n) where fmap = map instance I.Foldable (Vec n) where foldMap = foldMap foldr = foldr foldl' = foldl' #if MIN_VERSION_base(4,8,0) null = null length = length sum = sum product = product #endif instance n ~ 'S m => I.Foldable1 (Vec n) where foldMap1 = foldMap1 instance I.Traversable (Vec n) where traverse = traverse instance n ~ 'S m => I.Traversable1 (Vec n) where traverse1 = traverse1 instance NFData a => NFData (Vec n a) where rnf VNil = () rnf (x ::: xs) = rnf x `seq` rnf xs instance Hashable a => Hashable (Vec n a) where hashWithSalt salt VNil = hashWithSalt salt (0 :: Int) hashWithSalt salt (x ::: xs) = salt `hashWithSalt` x `hashWithSalt` xs instance N.SNatI n => Applicative (Vec n) where pure x = N.induction1 VNil (x :::) (<*>) = zipWith ($) _ *> x = x x <* _ = x #if MIN_VERSION_base(4,10,0) liftA2 = zipWith #endif instance N.SNatI n => Monad (Vec n) where return = pure (>>=) = bind _ >> x = x instance N.SNatI n => Distributive (Vec n) where distribute = distributeRep instance N.SNatI n => Representable (Vec n) where type Rep (Vec n) = Fin n tabulate = fromPull . tabulate index = index . toPull instance Semigroup a => Semigroup (Vec n a) where (<>) = zipWith (<>) instance (Monoid a, N.SNatI n) => Monoid (Vec n a) where mempty = pure mempty mappend = zipWith mappend instance n ~ 'N.Z => Boring (Vec n a) where boring = VNil instance Apply (Vec n) where (<.>) = zipWith ($) _ .> x = x x <. _ = x instance I.Bind (Vec n) where (>>-) = bind join = join instance I.FunctorWithIndex (Fin n) (Vec n) where imap = imap instance I.FoldableWithIndex (Fin n) (Vec n) where ifoldMap = ifoldMap ifoldr = ifoldr instance I.TraversableWithIndex (Fin n) (Vec n) where itraverse = itraverse instance I.Each (Vec n a) (Vec n b) a b where each = traverse type instance I.Index (Vec n a) = Fin n type instance I.IxValue (Vec n a) = a -- | 'Vec' doesn't have 'I.At' instance, as we __cannot__ remove value from 'Vec'. -- See 'ix' in "Data.Vec.Lazy" module for an 'I.Lens' (not 'I.Traversal'). instance I.Ixed (Vec n a) where ix = ix instance I.Field1 (Vec ('S n) a) (Vec ('S n) a) a a where _1 = _head instance I.Field2 (Vec ('S ('S n)) a) (Vec ('S ('S n)) a) a a where _2 = _tail . _head instance I.Field3 (Vec ('S ('S ('S n))) a) (Vec ('S ('S ('S n))) a) a a where _3 = _tail . _tail . _head instance I.Field4 (Vec ('S ('S ('S ('S n)))) a) (Vec ('S ('S ('S ('S n)))) a) a a where _4 = _tail . _tail . _tail . _head instance I.Field5 (Vec ('S ('S ('S ('S ('S n))))) a) (Vec ('S ('S ('S ('S ('S n))))) a) a a where _5 = _tail . _tail . _tail . _tail . _head instance I.Field6 (Vec ('S ('S ('S ('S ('S ('S n)))))) a) (Vec ('S ('S ('S ('S ('S ('S n)))))) a) a a where _6 = _tail . _tail . _tail . _tail . _tail . _head instance I.Field7 (Vec ('S ('S ('S ('S ('S ('S ('S n))))))) a) (Vec ('S ('S ('S ('S ('S ('S ('S n))))))) a) a a where _7 = _tail . _tail . _tail . _tail . _tail . _tail . _head instance I.Field8 (Vec ('S ('S ('S ('S ('S ('S ('S ('S n)))))))) a) (Vec ('S ('S ('S ('S ('S ('S ('S ('S n)))))))) a) a a where _8 = _tail . _tail . _tail . _tail . _tail . _tail . _tail . _head instance I.Field9 (Vec ('S ('S ('S ('S ('S ('S ('S ('S ('S n))))))))) a) (Vec ('S ('S ('S ('S ('S ('S ('S ('S ('S n))))))))) a) a a where _9 = _tail . _tail . _tail . _tail . _tail . _tail . _tail . _tail . _head ------------------------------------------------------------------------------- -- Construction ------------------------------------------------------------------------------- -- | Empty 'Vec'. empty :: Vec 'Z a empty = VNil -- | 'Vec' with exactly one element. -- -- >>> singleton True -- True ::: VNil -- singleton :: a -> Vec ('S 'Z) a singleton x = x ::: VNil -- | /O(n)/. Recover 'N.InlineInduction' (and 'N.SNatI') dictionary from a 'Vec' value. -- -- Example: 'N.reflect' is constrained with @'N.SNatI' n@, but if we have a -- @'Vec' n a@, we can recover that dictionary: -- -- >>> let f :: forall n a. Vec n a -> N.Nat; f v = withDict v (N.reflect (Proxy :: Proxy n)) in f (True ::: VNil) -- 1 -- -- /Note:/ using 'N.InlineInduction' will be suboptimal, as if GHC has no -- opportunity to optimise the code, the recusion won't be unfold. -- How bad such code will perform? I don't know, we'll need benchmarks. -- withDict :: Vec n a -> (N.InlineInduction n => r) -> r withDict VNil r = r withDict (_ ::: xs) r = withDict xs r ------------------------------------------------------------------------------- -- Conversions ------------------------------------------------------------------------------- -- | Convert to pull 'P.Vec'. toPull :: Vec n a -> P.Vec n a toPull VNil = P.Vec F.absurd toPull (x ::: xs) = P.Vec $ \n -> case n of F.Z -> x F.S m -> P.unVec (toPull xs) m -- | Convert from pull 'P.Vec'. fromPull :: forall n a. N.SNatI n => P.Vec n a -> Vec n a fromPull (P.Vec f) = case N.snat :: N.SNat n of N.SZ -> VNil N.SS -> f F.Z ::: fromPull (P.Vec (f . F.S)) -- | An 'I.Iso' from 'toPull' and 'fromPull'. _Pull :: N.SNatI n => I.Iso (Vec n a) (Vec n b) (P.Vec n a) (P.Vec n b) _Pull = I.iso toPull fromPull -- | Convert 'Vec' to list. -- -- >>> toList $ 'f' ::: 'o' ::: 'o' ::: VNil -- "foo" toList :: Vec n a -> [a] toList VNil = [] toList (x ::: xs) = x : toList xs -- | Convert list @[a]@ to @'Vec' n a@. -- Returns 'Nothing' if lengths don't match exactly. -- -- >>> fromList "foo" :: Maybe (Vec N.Nat3 Char) -- Just ('f' ::: 'o' ::: 'o' ::: VNil) -- -- >>> fromList "quux" :: Maybe (Vec N.Nat3 Char) -- Nothing -- -- >>> fromList "xy" :: Maybe (Vec N.Nat3 Char) -- Nothing -- fromList :: N.SNatI n => [a] -> Maybe (Vec n a) fromList = getFromList (N.induction1 start step) where start :: FromList 'Z a start = FromList $ \xs -> case xs of [] -> Just VNil (_ : _) -> Nothing step :: FromList n a -> FromList ('N.S n) a step (FromList f) = FromList $ \xs -> case xs of [] -> Nothing (x : xs') -> (x :::) <$> f xs' newtype FromList n a = FromList { getFromList :: [a] -> Maybe (Vec n a) } -- | Prism from list. -- -- >>> "foo" ^? _Vec :: Maybe (Vec N.Nat3 Char) -- Just ('f' ::: 'o' ::: 'o' ::: VNil) -- -- >>> "foo" ^? _Vec :: Maybe (Vec N.Nat2 Char) -- Nothing -- -- >>> _Vec # (True ::: False ::: VNil) -- [True,False] -- _Vec :: N.SNatI n => I.Prism' [a] (Vec n a) _Vec = I.prism' toList fromList -- | Convert list @[a]@ to @'Vec' n a@. -- Returns 'Nothing' if input list is too short. -- -- >>> fromListPrefix "foo" :: Maybe (Vec N.Nat3 Char) -- Just ('f' ::: 'o' ::: 'o' ::: VNil) -- -- >>> fromListPrefix "quux" :: Maybe (Vec N.Nat3 Char) -- Just ('q' ::: 'u' ::: 'u' ::: VNil) -- -- >>> fromListPrefix "xy" :: Maybe (Vec N.Nat3 Char) -- Nothing -- fromListPrefix :: N.SNatI n => [a] -> Maybe (Vec n a) fromListPrefix = getFromList (N.induction1 start step) where start :: FromList 'Z a start = FromList $ \_ -> Just VNil -- different than in fromList case step :: FromList n a -> FromList ('N.S n) a step (FromList f) = FromList $ \xs -> case xs of [] -> Nothing (x : xs') -> (x :::) <$> f xs' -- | Reify any list @[a]@ to @'Vec' n a@. -- -- >>> reifyList "foo" length -- 3 reifyList :: [a] -> (forall n. N.InlineInduction n => Vec n a -> r) -> r reifyList [] f = f VNil reifyList (x : xs) f = reifyList xs $ \xs' -> f (x ::: xs') ------------------------------------------------------------------------------- -- Indexing ------------------------------------------------------------------------------- -- | Indexing. -- -- >>> ('a' ::: 'b' ::: 'c' ::: VNil) ! F.S F.Z -- 'b' -- (!) :: Vec n a -> Fin n -> a (!) (x ::: _) (F.Z) = x (!) (_ ::: xs) (F.S n) = xs ! n (!) VNil n = case n of {} -- | Index lens. -- -- >>> ('a' ::: 'b' ::: 'c' ::: VNil) ^. ix (F.S F.Z) -- 'b' -- -- >>> ('a' ::: 'b' ::: 'c' ::: VNil) & ix (F.S F.Z) .~ 'x' -- 'a' ::: 'x' ::: 'c' ::: VNil -- ix :: Fin n -> I.Lens' (Vec n a) a ix F.Z f (x ::: xs) = (::: xs) <$> f x ix (F.S n) f (x ::: xs) = (x :::) <$> ix n f xs -- | Match on non-empty 'Vec'. -- -- /Note:/ @lens@ 'I._Cons' is a 'I.Prism'. -- In fact, @'Vec' n a@ cannot have an instance of 'I.Cons' as types don't match. -- _Cons :: I.Iso (Vec ('S n) a) (Vec ('S n) b) (a, Vec n a) (b, Vec n b) _Cons = I.iso (\(x ::: xs) -> (x, xs)) (\(x, xs) -> x ::: xs) -- | Head lens. /Note:/ @lens@ 'I._head' is a 'I.Traversal''. -- -- >>> ('a' ::: 'b' ::: 'c' ::: VNil) ^. _head -- 'a' -- -- >>> ('a' ::: 'b' ::: 'c' ::: VNil) & _head .~ 'x' -- 'x' ::: 'b' ::: 'c' ::: VNil -- _head :: I.Lens' (Vec ('S n) a) a _head f (x ::: xs) = (::: xs) <$> f x {-# INLINE head #-} -- | Head lens. /Note:/ @lens@ 'I._head' is a 'I.Traversal''. _tail :: I.Lens' (Vec ('S n) a) (Vec n a) _tail f (x ::: xs) = (x :::) <$> f xs {-# INLINE _tail #-} -- | Cons an element in front of a 'Vec'. cons :: a -> Vec n a -> Vec ('S n) a cons = (:::) -- | The first element of a 'Vec'. head :: Vec ('S n) a -> a head (x ::: _) = x -- | The elements after the 'head' of a 'Vec'. tail :: Vec ('S n) a -> Vec n a tail (_ ::: xs) = xs ------------------------------------------------------------------------------- -- Concatenation ------------------------------------------------------------------------------- infixr 5 ++ -- | Append two 'Vec'. -- -- >>> ('a' ::: 'b' ::: VNil) ++ ('c' ::: 'd' ::: VNil) -- 'a' ::: 'b' ::: 'c' ::: 'd' ::: VNil -- (++) :: Vec n a -> Vec m a -> Vec (N.Plus n m) a VNil ++ ys = ys (x ::: xs) ++ ys = x ::: xs ++ ys -- | Split vector into two parts. Inverse of '++'. -- -- >>> split ('a' ::: 'b' ::: 'c' ::: VNil) :: (Vec N.Nat1 Char, Vec N.Nat2 Char) -- ('a' ::: VNil,'b' ::: 'c' ::: VNil) -- -- >>> uncurry (++) (split ('a' ::: 'b' ::: 'c' ::: VNil) :: (Vec N.Nat1 Char, Vec N.Nat2 Char)) -- 'a' ::: 'b' ::: 'c' ::: VNil -- split :: N.SNatI n => Vec (N.Plus n m) a -> (Vec n a, Vec m a) split = appSplit (N.induction1 start step) where start :: Split m 'Z a start = Split $ \xs -> (VNil, xs) step :: Split m n a -> Split m ('S n) a step (Split f) = Split $ \(x ::: xs) -> case f xs of (ys, zs) -> (x ::: ys, zs) newtype Split m n a = Split { appSplit :: Vec (N.Plus n m) a -> (Vec n a, Vec m a) } -- | Map over all the elements of a 'Vec' and concatenate the resulting 'Vec's. -- -- >>> concatMap (\x -> x ::: x ::: VNil) ('a' ::: 'b' ::: VNil) -- 'a' ::: 'a' ::: 'b' ::: 'b' ::: VNil -- concatMap :: (a -> Vec m b) -> Vec n a -> Vec (N.Mult n m) b concatMap _ VNil = VNil concatMap f (x ::: xs) = f x ++ concatMap f xs -- | @'concatMap' 'id'@ concat :: Vec n (Vec m a) -> Vec (N.Mult n m) a concat = concatMap id -- | Inverse of 'concat'. -- -- >>> chunks <$> fromListPrefix [1..] :: Maybe (Vec N.Nat2 (Vec N.Nat3 Int)) -- Just ((1 ::: 2 ::: 3 ::: VNil) ::: (4 ::: 5 ::: 6 ::: VNil) ::: VNil) -- -- >>> let idVec x = x :: Vec N.Nat2 (Vec N.Nat3 Int) -- >>> concat . idVec . chunks <$> fromListPrefix [1..] -- Just (1 ::: 2 ::: 3 ::: 4 ::: 5 ::: 6 ::: VNil) -- chunks :: (N.SNatI n, N.SNatI m) => Vec (N.Mult n m) a -> Vec n (Vec m a) chunks = getChunks $ N.induction1 start step where start :: Chunks m 'Z a start = Chunks $ \_ -> VNil step :: forall m n a. N.SNatI m => Chunks m n a -> Chunks m ('S n) a step (Chunks go) = Chunks $ \xs -> let (ys, zs) = split xs :: (Vec m a, Vec (N.Mult n m) a) in ys ::: go zs newtype Chunks m n a = Chunks { getChunks :: Vec (N.Mult n m) a -> Vec n (Vec m a) } ------------------------------------------------------------------------------- -- Mapping ------------------------------------------------------------------------------- -- | >>> map not $ True ::: False ::: VNil -- False ::: True ::: VNil -- map :: (a -> b) -> Vec n a -> Vec n b map _ VNil = VNil map f (x ::: xs) = f x ::: fmap f xs -- | >>> imap (,) $ 'a' ::: 'b' ::: 'c' ::: VNil -- (0,'a') ::: (1,'b') ::: (2,'c') ::: VNil -- imap :: (Fin n -> a -> b) -> Vec n a -> Vec n b imap _ VNil = VNil imap f (x ::: xs) = f F.Z x ::: imap (f . F.S) xs -- | Apply an action to every element of a 'Vec', yielding a 'Vec' of results. traverse :: forall n f a b. Applicative f => (a -> f b) -> Vec n a -> f (Vec n b) traverse f = go where go :: Vec m a -> f (Vec m b) go VNil = pure VNil go (x ::: xs) = (:::) <$> f x <*> go xs -- | Apply an action to non-empty 'Vec', yielding a 'Vec' of results. traverse1 :: forall n f a b. Apply f => (a -> f b) -> Vec ('S n) a -> f (Vec ('S n) b) traverse1 f = go where go :: Vec ('S m) a -> f (Vec ('S m) b) go (x ::: VNil) = (::: VNil) <$> f x go (x ::: xs@(_ ::: _)) = (:::) <$> f x <.> go xs -- | Apply an action to every element of a 'Vec' and its index, yielding a 'Vec' of results. itraverse :: Applicative f => (Fin n -> a -> f b) -> Vec n a -> f (Vec n b) itraverse _ VNil = pure VNil itraverse f (x ::: xs) = (:::) <$> f F.Z x <*> I.itraverse (f . F.S) xs -- | Apply an action to every element of a 'Vec' and its index, ignoring the results. itraverse_ :: Applicative f => (Fin n -> a -> f b) -> Vec n a -> f () itraverse_ _ VNil = pure () itraverse_ f (x ::: xs) = f F.Z x *> itraverse_ (f . F.S) xs ------------------------------------------------------------------------------- -- Folding ------------------------------------------------------------------------------- -- | See 'I.Foldable'. foldMap :: Monoid m => (a -> m) -> Vec n a -> m foldMap _ VNil = mempty foldMap f (x ::: xs) = mappend (f x) (foldMap f xs) -- | See 'I.Foldable1'. foldMap1 :: Semigroup s => (a -> s) -> Vec ('S n) a -> s foldMap1 f (x ::: VNil) = f x foldMap1 f (x ::: xs@(_ ::: _)) = f x <> foldMap1 f xs -- | See 'I.FoldableWithIndex'. ifoldMap :: Monoid m => (Fin n -> a -> m) -> Vec n a -> m ifoldMap _ VNil = mempty ifoldMap f (x ::: xs) = mappend (f F.Z x) (ifoldMap (f . F.S) xs) -- | There is no type-class for this :( ifoldMap1 :: Semigroup s => (Fin ('S n) -> a -> s) -> Vec ('S n) a -> s ifoldMap1 f (x ::: VNil) = f F.Z x ifoldMap1 f (x ::: xs@(_ ::: _)) = f F.Z x <> ifoldMap1 (f . F.S) xs -- | Right fold. foldr :: forall a b n. (a -> b -> b) -> b -> Vec n a -> b foldr f z = go where go :: Vec m a -> b go VNil = z go (x ::: xs) = f x (go xs) -- | Right fold with an index. ifoldr :: forall a b n. (Fin n -> a -> b -> b) -> b -> Vec n a -> b ifoldr _ z VNil = z ifoldr f z (x ::: xs) = f F.Z x (ifoldr (f . F.S) z xs) -- | Strict left fold. foldl' :: forall a b n. (b -> a -> b) -> b -> Vec n a -> b foldl' f z = go z where go :: b -> Vec m a -> b go !acc VNil = acc go !acc (x ::: xs) = go (f acc x) xs -- | Yield the length of a 'Vec'. /O(n)/ length :: Vec n a -> Int length VNil = 0 length (_ ::: xs) = 1 + length xs -- | Test whether a 'Vec' is empty. /O(1)/ null :: Vec n a -> Bool null VNil = True null (_ ::: _) = False ------------------------------------------------------------------------------- -- Special folds ------------------------------------------------------------------------------- -- | Non-strict 'sum'. sum :: Num a => Vec n a -> a sum VNil = 0 sum (x ::: xs) = x + sum xs -- | Non-strict 'product'. product :: Num a => Vec n a -> a product VNil = 1 product (x ::: xs) = x * sum xs ------------------------------------------------------------------------------- -- Zipping ------------------------------------------------------------------------------- -- | Zip two 'Vec's with a function. zipWith :: (a -> b -> c) -> Vec n a -> Vec n b -> Vec n c zipWith _ VNil VNil = VNil zipWith f (x ::: xs) (y ::: ys) = f x y ::: zipWith f xs ys -- | Zip two 'Vec's. with a function that also takes the elements' indices. izipWith :: (Fin n -> a -> b -> c) -> Vec n a -> Vec n b -> Vec n c izipWith _ VNil VNil = VNil izipWith f (x ::: xs) (y ::: ys) = f F.Z x y ::: izipWith (f . F.S) xs ys ------------------------------------------------------------------------------- -- Monadic ------------------------------------------------------------------------------- -- | Monadic bind. bind :: Vec n a -> (a -> Vec n b) -> Vec n b bind VNil _ = VNil bind (x ::: xs) f = head (f x) ::: bind xs (tail . f) -- | Monadic join. -- -- >>> join $ ('a' ::: 'b' ::: VNil) ::: ('c' ::: 'd' ::: VNil) ::: VNil -- 'a' ::: 'd' ::: VNil join :: Vec n (Vec n a) -> Vec n a join VNil = VNil join (x ::: xs) = head x ::: join (map tail xs) ------------------------------------------------------------------------------- -- universe ------------------------------------------------------------------------------- -- | Get all @'Fin' n@ in a @'Vec' n@. -- -- >>> universe :: Vec N.Nat3 (Fin N.Nat3) -- 0 ::: 1 ::: 2 ::: VNil universe :: N.SNatI n => Vec n (Fin n) universe = getUniverse (N.induction first step) where first :: Universe 'Z first = Universe VNil step :: Universe m -> Universe ('S m) step (Universe go) = Universe (F.Z ::: map F.S go) newtype Universe n = Universe { getUniverse :: Vec n (Fin n) } ------------------------------------------------------------------------------- -- VecEach ------------------------------------------------------------------------------- -- | Write functions on 'Vec'. Use them with tuples. -- -- 'VecEach' can be used to avoid "this function won't change the length of the -- list" in DSLs. -- -- __bad:__ Instead of -- -- @ -- [x, y] <- badDslMagic ["foo", "bar"] -- list! -- @ -- -- __good:__ we can write -- -- @ -- (x, y) <- betterDslMagic ("foo", "bar") -- homogenic tuple! -- @ -- -- where @betterDslMagic@ can be defined using 'traverseWithVec'. -- class I.Each s t a b => VecEach s t a b | s -> a, t -> b, s b -> t, t a -> s where mapWithVec :: (forall n. N.InlineInduction n => Vec n a -> Vec n b) -> s -> t traverseWithVec :: Applicative f => (forall n. N.InlineInduction n => Vec n a -> f (Vec n b)) -> s -> f t instance (a ~ a', b ~ b') => VecEach (a, a') (b, b') a b where mapWithVec f ~(x, y) = case f (x ::: y ::: VNil) of x' ::: y' ::: VNil -> (x', y') traverseWithVec f ~(x, y) = f (x ::: y ::: VNil) <&> \res -> case res of x' ::: y' ::: VNil -> (x', y') instance (a ~ a2, a ~ a3, b ~ b2, b ~ b3) => VecEach (a, a2, a3) (b, b2, b3) a b where mapWithVec f ~(x, y, z) = case f (x ::: y ::: z ::: VNil) of x' ::: y' ::: z' ::: VNil -> (x', y', z') traverseWithVec f ~(x, y, z) = f (x ::: y ::: z ::: VNil) <&> \res -> case res of x' ::: y' ::: z' ::: VNil -> (x', y', z') instance (a ~ a2, a ~ a3, a ~ a4, b ~ b2, b ~ b3, b ~ b4) => VecEach (a, a2, a3, a4) (b, b2, b3, b4) a b where mapWithVec f ~(x, y, z, u) = case f (x ::: y ::: z ::: u ::: VNil) of x' ::: y' ::: z' ::: u' ::: VNil -> (x', y', z', u') traverseWithVec f ~(x, y, z, u) = f (x ::: y ::: z ::: u ::: VNil) <&> \res -> case res of x' ::: y' ::: z' ::: u' ::: VNil -> (x', y', z', u') ------------------------------------------------------------------------------- -- Doctest ------------------------------------------------------------------------------- -- $setup -- >>> :set -XScopedTypeVariables -- >>> import Control.Lens ((^.), (&), (.~), (^?), (#)) -- >>> import Data.Proxy (Proxy (..)) -- >>> import Prelude.Compat (Char, not, uncurry)