alignment 0.1.0.6 → 0.2.0.0
raw patch · 6 files changed
+1934/−714 lines, 6 filesdep +alignmentdep +containersdep +criteriondep ~basedep ~lensdep ~semigroupoidsPVP ok
version bump matches the API change (PVP)
Dependencies added: alignment, containers, criterion, deepseq, process, vector
Dependency ranges changed: base, lens, semigroupoids
API changes (from Hackage documentation)
- Data.Alignment: allThese :: forall (f :: Type -> Type) a b. Traversable f => Traversal' (This f a b) (a, b)
- Data.Alignment: allThese1 :: forall (f :: Type -> Type) a b. Traversable f => Traversal' (This f a b) a
- Data.Alignment: allThese2 :: forall (f :: Type -> Type) a b. Traversable f => Traversal' (This f a b) b
- Data.Alignment: allTheseThoseA :: forall (f :: Type -> Type) a b a'. Traversable f => Traversal (This f a b) (This f a' b) a a'
- Data.Alignment: allTheseThoseA1 :: forall (f :: Type -> Type) a b a'. Traversable1 f => Traversal1 (This f a b) (This f a' b) a a'
- Data.Alignment: allTheseThoseB :: forall (f :: Type -> Type) a b b'. Traversable f => Traversal (This f a b) (This f a b') b b'
- Data.Alignment: allTheseThoseB1 :: forall (f :: Type -> Type) a b b'. Traversable1 f => Traversal1 (This f a b) (This f a b') b b'
- Data.Alignment: allThose :: forall (f1 :: Type -> Type) a b f2. Applicative f2 => (Either (NonEmpty a) (NonEmpty b) -> f2 (Either (NonEmpty a) (NonEmpty b))) -> This f1 a b -> f2 (This f1 a b)
- Data.Alignment: allThoseA :: forall (f1 :: Type -> Type) a b f2. Applicative f2 => (NonEmpty a -> f2 (NonEmpty a)) -> This f1 a b -> f2 (This f1 a b)
- Data.Alignment: allThoseAOr :: forall (f1 :: Type -> Type) a b f2. Applicative f2 => ([a] -> f2 [a]) -> This f1 a b -> f2 (This f1 a b)
- Data.Alignment: allThoseB :: forall (f1 :: Type -> Type) a b f2. Applicative f2 => (NonEmpty b -> f2 (NonEmpty b)) -> This f1 a b -> f2 (This f1 a b)
- Data.Alignment: allThoseBOr :: forall (f1 :: Type -> Type) a b f2. Applicative f2 => ([b] -> f2 [b]) -> This f1 a b -> f2 (This f1 a b)
- Data.Alignment: instance (Data.Functor.Classes.Eq1 f, GHC.Classes.Eq a) => Data.Functor.Classes.Eq1 (Data.Alignment.This f a)
- Data.Alignment: instance (Data.Functor.Classes.Eq1 f, GHC.Classes.Eq a, GHC.Classes.Eq b) => GHC.Classes.Eq (Data.Alignment.This f a b)
- Data.Alignment: instance (Data.Functor.Classes.Ord1 f, GHC.Classes.Ord a) => Data.Functor.Classes.Ord1 (Data.Alignment.This f a)
- Data.Alignment: instance (Data.Functor.Classes.Ord1 f, GHC.Classes.Ord a, GHC.Classes.Ord b) => GHC.Classes.Ord (Data.Alignment.This f a b)
- Data.Alignment: instance (Data.Functor.Classes.Show1 f, GHC.Show.Show a) => Data.Functor.Classes.Show1 (Data.Alignment.This f a)
- Data.Alignment: instance (Data.Functor.Classes.Show1 f, GHC.Show.Show a, GHC.Show.Show b) => GHC.Show.Show (Data.Alignment.This f a b)
- Data.Alignment: instance (GHC.Base.Monoid a, GHC.Base.Applicative f) => GHC.Base.Applicative (Data.Alignment.This f a)
- Data.Alignment: instance (GHC.Base.Semigroup a, Data.Functor.Bind.Class.Apply f) => Data.Functor.Bind.Class.Apply (Data.Alignment.This f a)
- Data.Alignment: instance Data.Alignment.Align Control.Applicative.ZipList
- Data.Alignment: instance Data.Alignment.Align GHC.Maybe.Maybe
- Data.Alignment: instance Data.Alignment.Align []
- Data.Alignment: instance Data.Alignment.Semialign Control.Applicative.ZipList
- Data.Alignment: instance Data.Alignment.Semialign Data.Functor.Identity.Identity
- Data.Alignment: instance Data.Alignment.Semialign GHC.Base.NonEmpty
- Data.Alignment: instance Data.Alignment.Semialign GHC.Maybe.Maybe
- Data.Alignment: instance Data.Alignment.Semialign []
- Data.Alignment: instance Data.Foldable.Foldable f => Data.Bifoldable.Bifoldable (Data.Alignment.This f)
- Data.Alignment: instance Data.Foldable1.Foldable1 f => Data.Bifoldable1.Bifoldable1 (Data.Alignment.This f)
- Data.Alignment: instance Data.Semigroup.Traversable.Class.Traversable1 f => Data.Semigroup.Traversable.Class.Bitraversable1 (Data.Alignment.This f)
- Data.Alignment: instance Data.Traversable.Traversable f => Data.Bitraversable.Bitraversable (Data.Alignment.This f)
- Data.Alignment: instance GHC.Base.Functor f => Data.Bifunctor.Bifunctor (Data.Alignment.This f)
- Data.Alignment: instance GHC.Base.Functor f => Data.Bifunctor.Swap.Swap (Data.Alignment.This f)
- Data.Alignment: instance GHC.Base.Functor f => GHC.Base.Functor (Data.Alignment.This f a)
- Data.Alignment: instance GHC.Base.Monoid (Data.Alignment.This [] a b)
- Data.Alignment: instance GHC.Base.Semigroup (Data.Alignment.This GHC.Base.NonEmpty a b)
- Data.Alignment: instance GHC.Base.Semigroup (Data.Alignment.This [] a b)
+ Data.Alignment: _This :: AsThis s f g a b => Prism' s (This f g a b)
+ Data.Alignment: alignEmpty :: forall f (g :: Type -> Type) a. (Align f g, Eq1 f, Eq1 g, Eq a) => f a -> f a -> Bool
+ Data.Alignment: alignLeftIdentity :: forall f (g :: Type -> Type) a b. (Align f g, Eq1 f, Eq a, Eq b) => f a -> f b -> Bool
+ Data.Alignment: alignRightIdentity :: forall f (g :: Type -> Type) a b. (Align f g, Eq1 f, Eq a, Eq b) => f a -> f b -> Bool
+ Data.Alignment: aligned :: Unalign f g => Iso' (f a, f b) (This f g a b)
+ Data.Alignment: class ReviewThis s f g a b => AsThis s (f :: Type -> Type) (g :: Type -> Type) a b | s -> f g a b
+ Data.Alignment: class GetThis s (f :: Type -> Type) (g :: Type -> Type) a b | s -> f g a b
+ Data.Alignment: class GetThis s f g a b => HasThis s (f :: Type -> Type) (g :: Type -> Type) a b | s -> f g a b
+ Data.Alignment: class ReviewThis s (f :: Type -> Type) (g :: Type -> Type) a b | s -> f g a b
+ Data.Alignment: class Semialign f g => Unalign (f :: Type -> Type) (g :: Type -> Type)
+ Data.Alignment: foldA :: forall (f :: Type -> Type) (g :: Type -> Type) a b. (Foldable f, Foldable g) => Fold (This f g a b) a
+ Data.Alignment: foldA1 :: forall (f :: Type -> Type) (g :: Type -> Type) a b. (Foldable1 f, Foldable1 g) => Fold1 (This f g a b) a
+ Data.Alignment: foldB :: forall (f :: Type -> Type) (g :: Type -> Type) a b. (Foldable f, Foldable g) => Fold (This f g a b) b
+ Data.Alignment: foldB1 :: forall (f :: Type -> Type) (g :: Type -> Type) a b. (Foldable1 f, Foldable1 g) => Fold1 (This f g a b) b
+ Data.Alignment: getThis :: GetThis s f g a b => Getter s (This f g a b)
+ Data.Alignment: instance (Control.DeepSeq.NFData (f (a, b)), Control.DeepSeq.NFData (g a), Control.DeepSeq.NFData (g b)) => Control.DeepSeq.NFData (Data.Alignment.This f g a b)
+ Data.Alignment: instance (Data.Foldable.Foldable f, Data.Foldable.Foldable g) => Data.Bifoldable.Bifoldable (Data.Alignment.This f g)
+ Data.Alignment: instance (Data.Foldable.Foldable f, Data.Foldable.Foldable g) => Data.Foldable.Foldable (Data.Alignment.This f g a)
+ Data.Alignment: instance (Data.Foldable1.Foldable1 f, Data.Foldable1.Foldable1 g) => Data.Bifoldable1.Bifoldable1 (Data.Alignment.This f g)
+ Data.Alignment: instance (Data.Foldable1.Foldable1 f, Data.Foldable1.Foldable1 g) => Data.Foldable1.Foldable1 (Data.Alignment.This f g a)
+ Data.Alignment: instance (Data.Functor.Classes.Eq1 f, Data.Functor.Classes.Eq1 g) => Data.Functor.Classes.Eq2 (Data.Alignment.This f g)
+ Data.Alignment: instance (Data.Functor.Classes.Eq1 f, Data.Functor.Classes.Eq1 g, GHC.Classes.Eq a) => Data.Functor.Classes.Eq1 (Data.Alignment.This f g a)
+ Data.Alignment: instance (Data.Functor.Classes.Eq1 f, Data.Functor.Classes.Eq1 g, GHC.Classes.Eq a, GHC.Classes.Eq b) => GHC.Classes.Eq (Data.Alignment.This f g a b)
+ Data.Alignment: instance (Data.Functor.Classes.Ord1 f, Data.Functor.Classes.Ord1 g) => Data.Functor.Classes.Ord2 (Data.Alignment.This f g)
+ Data.Alignment: instance (Data.Functor.Classes.Ord1 f, Data.Functor.Classes.Ord1 g, GHC.Classes.Ord a) => Data.Functor.Classes.Ord1 (Data.Alignment.This f g a)
+ Data.Alignment: instance (Data.Functor.Classes.Ord1 f, Data.Functor.Classes.Ord1 g, GHC.Classes.Ord a, GHC.Classes.Ord b) => GHC.Classes.Ord (Data.Alignment.This f g a b)
+ Data.Alignment: instance (Data.Functor.Classes.Show1 f, Data.Functor.Classes.Show1 g) => Data.Functor.Classes.Show2 (Data.Alignment.This f g)
+ Data.Alignment: instance (Data.Functor.Classes.Show1 f, Data.Functor.Classes.Show1 g, GHC.Show.Show a) => Data.Functor.Classes.Show1 (Data.Alignment.This f g a)
+ Data.Alignment: instance (Data.Functor.Classes.Show1 f, Data.Functor.Classes.Show1 g, GHC.Show.Show a, GHC.Show.Show b) => GHC.Show.Show (Data.Alignment.This f g a b)
+ Data.Alignment: instance (Data.Semigroup.Traversable.Class.Traversable1 f, Data.Semigroup.Traversable.Class.Traversable1 g) => Data.Semigroup.Traversable.Class.Bitraversable1 (Data.Alignment.This f g)
+ Data.Alignment: instance (Data.Semigroup.Traversable.Class.Traversable1 f, Data.Semigroup.Traversable.Class.Traversable1 g) => Data.Semigroup.Traversable.Class.Traversable1 (Data.Alignment.This f g a)
+ Data.Alignment: instance (Data.Traversable.Traversable f, Data.Traversable.Traversable g) => Data.Bitraversable.Bitraversable (Data.Alignment.This f g)
+ Data.Alignment: instance (Data.Traversable.Traversable f, Data.Traversable.Traversable g) => Data.Traversable.Traversable (Data.Alignment.This f g a)
+ Data.Alignment: instance (GHC.Base.Functor f, GHC.Base.Functor g) => Data.Bifunctor.Bifunctor (Data.Alignment.This f g)
+ Data.Alignment: instance (GHC.Base.Functor f, GHC.Base.Functor g) => GHC.Base.Functor (Data.Alignment.This f g a)
+ Data.Alignment: instance (GHC.Base.Monoid (f (a, b)), GHC.Base.Semigroup (g a), GHC.Base.Semigroup (g b)) => GHC.Base.Monoid (Data.Alignment.This f g a b)
+ Data.Alignment: instance (GHC.Base.Semigroup (f (a, b)), GHC.Base.Semigroup (g a), GHC.Base.Semigroup (g b)) => GHC.Base.Semigroup (Data.Alignment.This f g a b)
+ Data.Alignment: instance Data.Alignment.Align Control.Applicative.ZipList GHC.Base.NonEmpty
+ Data.Alignment: instance Data.Alignment.Align Data.IntMap.Internal.IntMap Data.IntMap.Internal.IntMap
+ Data.Alignment: instance Data.Alignment.Align Data.Sequence.Internal.Seq GHC.Base.NonEmpty
+ Data.Alignment: instance Data.Alignment.Align Data.Vector.Vector GHC.Base.NonEmpty
+ Data.Alignment: instance Data.Alignment.Align GHC.Maybe.Maybe Data.Functor.Identity.Identity
+ Data.Alignment: instance Data.Alignment.Align [] GHC.Base.NonEmpty
+ Data.Alignment: instance Data.Alignment.AsThis (Data.Alignment.This f g a b) f g a b
+ Data.Alignment: instance Data.Alignment.GetThis (Data.Alignment.This f g a b) f g a b
+ Data.Alignment: instance Data.Alignment.HasThis (Data.Alignment.This f g a b) f g a b
+ Data.Alignment: instance Data.Alignment.ReviewThis (Data.Alignment.This f g a b) f g a b
+ Data.Alignment: instance Data.Alignment.Semialign ((->) r) Data.Functor.Identity.Identity
+ Data.Alignment: instance Data.Alignment.Semialign (Data.Functor.Const.Const m) Data.Functor.Identity.Identity
+ Data.Alignment: instance Data.Alignment.Semialign Control.Applicative.ZipList GHC.Base.NonEmpty
+ Data.Alignment: instance Data.Alignment.Semialign Data.Functor.Identity.Identity Data.Functor.Identity.Identity
+ Data.Alignment: instance Data.Alignment.Semialign Data.IntMap.Internal.IntMap Data.IntMap.Internal.IntMap
+ Data.Alignment: instance Data.Alignment.Semialign Data.Sequence.Internal.Seq GHC.Base.NonEmpty
+ Data.Alignment: instance Data.Alignment.Semialign Data.Vector.Vector GHC.Base.NonEmpty
+ Data.Alignment: instance Data.Alignment.Semialign GHC.Base.NonEmpty GHC.Base.NonEmpty
+ Data.Alignment: instance Data.Alignment.Semialign GHC.Maybe.Maybe Data.Functor.Identity.Identity
+ Data.Alignment: instance Data.Alignment.Semialign [] GHC.Base.NonEmpty
+ Data.Alignment: instance Data.Alignment.Unalign Control.Applicative.ZipList GHC.Base.NonEmpty
+ Data.Alignment: instance Data.Alignment.Unalign Data.Functor.Identity.Identity Data.Functor.Identity.Identity
+ Data.Alignment: instance Data.Alignment.Unalign Data.Sequence.Internal.Seq GHC.Base.NonEmpty
+ Data.Alignment: instance Data.Alignment.Unalign Data.Vector.Vector GHC.Base.NonEmpty
+ Data.Alignment: instance Data.Alignment.Unalign GHC.Base.NonEmpty GHC.Base.NonEmpty
+ Data.Alignment: instance Data.Alignment.Unalign GHC.Maybe.Maybe Data.Functor.Identity.Identity
+ Data.Alignment: instance Data.Alignment.Unalign [] GHC.Base.NonEmpty
+ Data.Alignment: instance GHC.Base.Functor f => Data.Bifunctor.Swap.Swap (Data.Alignment.This f g)
+ Data.Alignment: instance GHC.Base.Functor f => GHC.Generics.Generic1 (Data.Alignment.This f g a)
+ Data.Alignment: instance GHC.Base.Monoid e => Data.Alignment.Semialign ((,) e) Data.Functor.Identity.Identity
+ Data.Alignment: instance GHC.Base.Monoid m => Data.Alignment.Align (Data.Functor.Const.Const m) Data.Functor.Identity.Identity
+ Data.Alignment: instance GHC.Classes.Ord k => Data.Alignment.Align (Data.Map.Internal.Map k) (Data.Map.Internal.Map k)
+ Data.Alignment: instance GHC.Classes.Ord k => Data.Alignment.Semialign (Data.Map.Internal.Map k) (Data.Map.Internal.Map k)
+ Data.Alignment: instance GHC.Generics.Generic (Data.Alignment.This f g a b)
+ Data.Alignment: matchThis :: AsThis s f g a b => s -> Maybe (This f g a b)
+ Data.Alignment: reviewThis :: ReviewThis s f g a b => Review s (This f g a b)
+ Data.Alignment: semialignCoherence :: forall f (g :: Type -> Type) a b. (Semialign f g, Eq1 f, Eq1 g, Eq a, Eq b) => f a -> f b -> Bool
+ Data.Alignment: semialignNaturality :: forall f (g :: Type -> Type) c d a b. (Semialign f g, Eq1 f, Eq1 g, Eq c, Eq d) => (a -> c) -> (b -> d) -> f a -> f b -> Bool
+ Data.Alignment: semialignSymmetry :: forall f (g :: Type -> Type) a b. (Semialign f g, Eq1 f, Eq1 g, Eq a, Eq b) => f a -> f b -> Bool
+ Data.Alignment: semialignWithLaw :: forall f (g :: Type -> Type) c d a b. (Semialign f g, Eq1 f, Eq1 g, Eq c, Eq d) => ((a, b) -> (c, d)) -> (a -> c) -> (b -> d) -> f a -> f b -> Bool
+ Data.Alignment: setThis :: HasThis s f g a b => This f g a b -> s -> s
+ Data.Alignment: this' :: HasThis s f g a b => Lens' s (This f g a b)
+ Data.Alignment: thoseLeft :: forall (f1 :: Type -> Type) g a b f2. Applicative f2 => (g a -> f2 (g a)) -> This f1 g a b -> f2 (This f1 g a b)
+ Data.Alignment: thoseRight :: forall (f1 :: Type -> Type) g a b f2. Applicative f2 => (g b -> f2 (g b)) -> This f1 g a b -> f2 (This f1 g a b)
+ Data.Alignment: traverseA :: forall (f :: Type -> Type) (g :: Type -> Type) a b a'. (Traversable f, Traversable g) => Traversal (This f g a b) (This f g a' b) a a'
+ Data.Alignment: traverseA1 :: forall (f :: Type -> Type) (g :: Type -> Type) a b a'. (Traversable1 f, Traversable1 g) => Traversal1 (This f g a b) (This f g a' b) a a'
+ Data.Alignment: traverseB :: forall (f :: Type -> Type) (g :: Type -> Type) a b b'. (Traversable f, Traversable g) => Traversal (This f g a b) (This f g a b') b b'
+ Data.Alignment: traverseB1 :: forall (f :: Type -> Type) (g :: Type -> Type) a b b'. (Traversable1 f, Traversable1 g) => Traversal1 (This f g a b) (This f g a b') b b'
+ Data.Alignment: type This' (f :: Type -> Type) a b = This f f a b
+ Data.Alignment: unalign :: Unalign f g => This f g a b -> (f a, f b)
+ Data.Alignment: unalignNaturality :: forall (f :: Type -> Type) (g :: Type -> Type) c d a b. (Unalign f g, Eq1 f, Eq c, Eq d) => (a -> c) -> (b -> d) -> This f g a b -> Bool
+ Data.Alignment: unalignRoundtrip :: forall f (g :: Type -> Type) a b. (Unalign f g, Eq1 f, Eq a, Eq b) => f a -> f b -> Bool
+ Data.Alignment: unalignWith :: Unalign f g => (a -> c) -> (b -> d) -> This f g a b -> (f c, f d)
+ Data.Alignment: unaligned :: forall (f :: Type -> Type) (g :: Type -> Type) a b. Unalign f g => Iso' (This f g a b) (f a, f b)
- Data.Alignment: This :: f (a, b) -> Maybe (Either (NonEmpty a) (NonEmpty b)) -> This (f :: Type -> Type) a b
+ Data.Alignment: This :: f (a, b) -> Maybe (Either (g a) (g b)) -> This (f :: Type -> Type) (g :: Type -> Type) a b
- Data.Alignment: align :: Semialign f => f a -> f b -> This f a b
+ Data.Alignment: align :: Semialign f g => f a -> f b -> This f g a b
- Data.Alignment: alignWith :: Semialign f => ((a, b) -> (c, d)) -> (a -> c) -> (b -> d) -> f a -> f b -> This f c d
+ Data.Alignment: alignWith :: Semialign f g => ((a, b) -> (c, d)) -> (a -> c) -> (b -> d) -> f a -> f b -> This f g c d
- Data.Alignment: alignWith' :: Semialign f => (a -> c) -> (b -> d) -> f a -> f b -> This f c d
+ Data.Alignment: alignWith' :: Semialign f g => (a -> c) -> (b -> d) -> f a -> f b -> This f g c d
- Data.Alignment: class Semialign f => Align (f :: Type -> Type)
+ Data.Alignment: class Semialign f g => Align (f :: Type -> Type) (g :: Type -> Type)
- Data.Alignment: class Functor f => Semialign (f :: Type -> Type)
+ Data.Alignment: class (Functor f, Functor g) => Semialign (f :: Type -> Type) (g :: Type -> Type) | f -> g
- Data.Alignment: data This (f :: Type -> Type) a b
+ Data.Alignment: data This (f :: Type -> Type) (g :: Type -> Type) a b
- Data.Alignment: nil :: Align f => f a
+ Data.Alignment: nil :: Align f g => f a
- Data.Alignment: these :: forall f1 a b f' f2. Functor f2 => (f1 (a, b) -> f2 (f' (a, b))) -> This f1 a b -> f2 (This f' a b)
+ Data.Alignment: these :: forall f1 (g :: Type -> Type) a b f' f2. Functor f2 => (f1 (a, b) -> f2 (f' (a, b))) -> This f1 g a b -> f2 (This f' g a b)
- Data.Alignment: those :: forall (f1 :: Type -> Type) a b f2. Functor f2 => (Maybe (Either (NonEmpty a) (NonEmpty b)) -> f2 (Maybe (Either (NonEmpty a) (NonEmpty b)))) -> This f1 a b -> f2 (This f1 a b)
+ Data.Alignment: those :: forall (f1 :: Type -> Type) g a b g' f2. Functor f2 => (Maybe (Either (g a) (g b)) -> f2 (Maybe (Either (g' a) (g' b)))) -> This f1 g a b -> f2 (This f1 g' a b)
Files
- LICENCE +1/−1
- alignment.cabal +106/−32
- bench/Main.hs +160/−0
- changelog.md +31/−0
- src/Data/Alignment.hs +1619/−681
- test/Main.hs +17/−0
LICENCE view
@@ -1,4 +1,4 @@-Copyright (c) 2022-2025 Tony Morris+Copyright (c) 2022-2026 Tony Morris All rights reserved.
alignment.cabal view
@@ -1,38 +1,112 @@--- documentation, see http://haskell.org/cabal/users-guide/--name: alignment-version: 0.1.0.6-synopsis: Zip-alignment+cabal-version: 2.4+name: alignment+version: 0.2.0.0+synopsis: Principled functor alignment with leftovers description:- Zipping with alignment- .- <<https://logo.systemf.com.au/systemf-450x450.png>>-license: BSD3-license-file: LICENCE-author: Tony Morris <oᴉ˙ldɟb@llǝʞsɐɥ>-maintainer: Tony Morris <oᴉ˙ldɟb@llǝʞsɐɥ>-copyright: Copyright (C) 2022-2025 Tony Morris-category: Data-build-type: Simple-extra-source-files: changelog.md-cabal-version: >=1.10-homepage: https://gitlab.com/system-f/code/alignment-bug-reports: https://gitlab.com/system-f/code/alignment/issues-tested-with: GHC == 7.10.3, GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.3, GHC == 8.6.1, GHC == 9.4.8+ A principled approach to zipping functors that preserves both matched+ pairs and leftovers.+ .+ The @alignment@ library provides type classes and operations for aligning+ two functors into a structure (@This@) that captures:+ .+ * Matched pairs where both functors have elements at the same position+ .+ * Leftovers when one functor is longer than the other+ .+ This is more principled than traditional @zip@ which silently discards+ extra elements. The library uses functional dependencies (@f -> g@) to+ relate the \"paired\" functor to the \"leftover\" functor, ensuring type safety.+ .+ Key features:+ .+ * @Semialign@, @Align@, and @Unalign@ type classes with comprehensive laws+ .+ * @Unalign@ provides inverse operation to recover original functors+ .+ * Full lens\/optics integration+ .+ * Instances for common functors: @[]@, @Maybe@, @NonEmpty@, @Vector@,+ @Map@, @Seq@, and more+ .+ * Testable law-checking functions for property-based testing+ .+ * Complete documentation with 211 doctests+ .+ Example:+ .+ > import Data.Alignment+ >+ > -- Align two lists of different lengths+ > align [1,2,3] [10,20] :: This [] NonEmpty Int Int+ > -- Result: This [(1,10),(2,20)] (Just (Left (3 :| [])))+ .+ <<https://logo.systemf.com.au/systemf-450x450.png>>+license: BSD-3-Clause+license-file: LICENCE+author: Tony Morris <oᴉ˙ldɟb@llǝʞsɐɥ>+maintainer: Tony Morris <oᴉ˙ldɟb@llǝʞsɐɥ>+copyright: Copyright (C) 2022-2026 Tony Morris+category: Data+build-type: Simple+extra-doc-files: changelog.md+homepage: https://gitlab.com/system-f/code/alignment+bug-reports: https://gitlab.com/system-f/code/alignment/issues+tested-with: GHC == 9.6.7 -source-repository head- type: git- location: git@gitlab.com:system-f/code/alignment.git+source-repository head+ type: git+ location: git@gitlab.com:system-f/code/alignment.git library- exposed-modules: Data.Alignment+ exposed-modules: Data.Alignment - build-depends: base >= 4.8 && < 6- , bifunctors >= 5 && < 6- , lens >= 4.15 && < 6- , semigroupoids >= 5.1 && < 7- , assoc >= 1 && < 2+ build-depends: base >= 4.18 && < 6+ , bifunctors >= 5 && < 6+ , deepseq >= 1.4 && < 1.6+ , lens >= 4 && < 6+ , semigroupoids >= 6 && < 7+ , assoc >= 1 && < 2+ , containers >= 0.6 && < 0.8+ , vector >= 0.12 && < 0.14 - hs-source-dirs: src- default-language: Haskell2010- ghc-options: -Wall+ hs-source-dirs: src++ default-language: Haskell2010++ ghc-options: -Wall++ if flag(dev)+ ghc-options: -Werror++flag dev+ description: Enable -Werror for development+ manual: True+ default: False++test-suite doctest+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Main.hs+ build-depends: base >= 4.8 && < 6+ , process >= 1 && < 2+ build-tool-depends: doctest:doctest >= 0.22+ default-language: Haskell2010+ ghc-options: -Wall+ -Wno-inline-rule-shadowing++benchmark alignment-bench+ type: exitcode-stdio-1.0+ hs-source-dirs: bench+ main-is: Main.hs+ build-depends: base >= 4.8 && < 6+ , alignment+ , criterion >= 1.5 && < 1.7+ , deepseq >= 1.4 && < 1.6+ , vector >= 0.12 && < 0.14+ , containers >= 0.6 && < 0.8+ default-language: Haskell2010+ ghc-options: -Wall+ -O2+ -threaded+ -rtsopts+ -with-rtsopts=-N
+ bench/Main.hs view
@@ -0,0 +1,160 @@+{-# LANGUAGE ScopedTypeVariables #-}+{- HLINT ignore "Avoid NonEmpty.unzip" -}++module Main where++import Criterion.Main+import Data.Alignment+import qualified Data.Vector as V+import Data.List.NonEmpty (NonEmpty(..))+import Control.DeepSeq (NFData, force)+import Data.Bifunctor (bimap)++-- Force evaluation to prevent benchmark cheating+forceThis :: (NFData (f (a, b)), NFData (g a), NFData (g b)) => This f g a b -> This f g a b+forceThis (This pairs leftover) = This (force pairs) (force leftover)++-- Benchmark groups+main :: IO ()+main = defaultMain+ [ alignBenchmarks+ , unalignBenchmarks+ , roundtripBenchmarks+ , transformationBenchmarks+ , fusionBenchmarks+ ]++-- | Benchmark align vs zip for different sizes and structures+alignBenchmarks :: Benchmark+alignBenchmarks = bgroup "align vs zip"+ [ bgroup "lists"+ [ bgroup "equal length"+ [ bench "zip 100" $ nf (uncurry zip) (listPair 100)+ , bench "align 100" $ nf (uncurry alignList) (listPair 100)+ , bench "zip 1000" $ nf (uncurry zip) (listPair 1000)+ , bench "align 1000" $ nf (uncurry alignList) (listPair 1000)+ , bench "zip 10000" $ nf (uncurry zip) (listPair 10000)+ , bench "align 10000" $ nf (uncurry alignList) (listPair 10000)+ ]+ , bgroup "unequal length"+ [ bench "zip 100/50" $ nf (\(xs, ys) -> zip xs (take 50 ys)) (listPair 100)+ , bench "align 100/50" $ nf (\(xs, ys) -> alignList xs (take 50 ys)) (listPair 100)+ , bench "zip 1000/500" $ nf (\(xs, ys) -> zip xs (take 500 ys)) (listPair 1000)+ , bench "align 1000/500" $ nf (\(xs, ys) -> alignList xs (take 500 ys)) (listPair 1000)+ ]+ ]+ , bgroup "vectors"+ [ bgroup "equal length"+ [ bench "zip 100" $ nf (uncurry V.zip) (vectorPair 100)+ , bench "align 100" $ nf (uncurry alignVec) (vectorPair 100)+ , bench "zip 1000" $ nf (uncurry V.zip) (vectorPair 1000)+ , bench "align 1000" $ nf (uncurry alignVec) (vectorPair 1000)+ , bench "zip 10000" $ nf (uncurry V.zip) (vectorPair 10000)+ , bench "align 10000" $ nf (uncurry alignVec) (vectorPair 10000)+ ]+ ]+ , bgroup "NonEmpty"+ [ bench "align 100" $ nf (uncurry alignNE) (nePair 100)+ , bench "align 1000" $ nf (uncurry alignNE) (nePair 1000)+ ]+ ]+ where+ alignList :: [Int] -> [Int] -> This [] NonEmpty Int Int+ alignList = align+ alignVec :: V.Vector Int -> V.Vector Int -> This V.Vector NonEmpty Int Int+ alignVec = align+ alignNE :: NonEmpty Int -> NonEmpty Int -> This NonEmpty NonEmpty Int Int+ alignNE = align++-- | Benchmark unalign vs unzip+unalignBenchmarks :: Benchmark+unalignBenchmarks = bgroup "unalign vs unzip"+ [ bgroup "lists"+ [ bench "unzip 100" $ nf unzip (pairList 100)+ , bench "unalign 100" $ nf unalignList (alignedList 100)+ , bench "unzip 1000" $ nf unzip (pairList 1000)+ , bench "unalign 1000" $ nf unalignList (alignedList 1000)+ , bench "unzip 10000" $ nf unzip (pairList 10000)+ , bench "unalign 10000" $ nf unalignList (alignedList 10000)+ ]+ , bgroup "vectors"+ [ bench "unzip 100" $ nf V.unzip (V.fromList $ pairList 100)+ , bench "unalign 100" $ nf unalignVec (alignedVector 100)+ , bench "unzip 1000" $ nf V.unzip (V.fromList $ pairList 1000)+ , bench "unalign 1000" $ nf unalignVec (alignedVector 1000)+ , bench "unzip 10000" $ nf V.unzip (V.fromList $ pairList 10000)+ , bench "unalign 10000" $ nf unalignVec (alignedVector 10000)+ ]+ ]+ where+ unalignList :: This [] NonEmpty Int Int -> ([Int], [Int])+ unalignList = unalign+ unalignVec :: This V.Vector NonEmpty Int Int -> (V.Vector Int, V.Vector Int)+ unalignVec = unalign++-- | Benchmark roundtrip: align then unalign+roundtripBenchmarks :: Benchmark+roundtripBenchmarks = bgroup "roundtrip"+ [ bgroup "lists"+ [ bench "zip/unzip 100" $ nf (\(xs, ys) -> unzip (zip xs ys)) (listPair 100)+ , bench "align/unalign 100" $ nf (\(xs, ys) -> unalign (align xs ys :: This [] NonEmpty Int Int)) (listPair 100)+ , bench "zip/unzip 1000" $ nf (\(xs, ys) -> unzip (zip xs ys)) (listPair 1000)+ , bench "align/unalign 1000" $ nf (\(xs, ys) -> unalign (align xs ys :: This [] NonEmpty Int Int)) (listPair 1000)+ ]+ , bgroup "vectors"+ [ bench "zip/unzip 100" $ nf (\(xs, ys) -> V.unzip (V.zip xs ys)) (vectorPair 100)+ , bench "align/unalign 100" $ nf (\(xs, ys) -> unalign (align xs ys :: This V.Vector NonEmpty Int Int)) (vectorPair 100)+ , bench "zip/unzip 1000" $ nf (\(xs, ys) -> V.unzip (V.zip xs ys)) (vectorPair 1000)+ , bench "align/unalign 1000" $ nf (\(xs, ys) -> unalign (align xs ys :: This V.Vector NonEmpty Int Int)) (vectorPair 1000)+ ]+ ]++-- | Benchmark transformation operations (map during align/unalign)+transformationBenchmarks :: Benchmark+transformationBenchmarks = bgroup "with transformation"+ [ bgroup "lists"+ [ bench "map/zip/map 1000" $ nf (\(xs, ys) -> let zs = zip xs ys in (map ((+1) . fst) zs, map ((*2) . snd) zs)) (listPair 1000)+ , bench "alignWith 1000" $ nf (\(xs, ys) -> alignWith id (+1) (*2) xs ys :: This [] NonEmpty Int Int) (listPair 1000)+ , bench "unzip/map/map 1000" $ nf (bimap (map (+1)) (map (*2)) . unzip) (pairList 1000)+ , bench "unalignWith 1000" $ nf (unalignWith (+1) (*2)) (alignedList 1000)+ ]+ ]++-- | Benchmark fusion effectiveness+fusionBenchmarks :: Benchmark+fusionBenchmarks = bgroup "fusion"+ [ bgroup "composition"+ -- These benchmarks intentionally compare unoptimized vs optimized forms+ {- HLINT ignore "Functor law" -}+ {- HLINT ignore "Redundant bimap" -}+ [ bench "fmap . fmap 1000" $ nf (fmap (*2) . fmap (+1)) (alignedList 1000)+ , bench "fmap composed 1000" $ nf (fmap ((*2) . (+1))) (alignedList 1000)+ , bench "bimap . bimap 1000" $ nf (bimap (*2) (*3) . bimap (+1) (+2)) (alignedList 1000)+ , bench "bimap composed 1000" $ nf (bimap ((*2) . (+1)) ((*3) . (+2))) (alignedList 1000)+ ]+ , bgroup "roundtrip elimination"+ [ bench "align/unalign 1000" $ nf (\(xs, ys) -> unalign (align xs ys :: This [] NonEmpty Int Int)) (listPair 1000)+ , bench "direct 1000" $ nf id (listPair 1000)+ , bench "alignWith/unalignWith 1000" $ nf (\(xs, ys) -> unalignWith (+1) (*2) (align xs ys :: This [] NonEmpty Int Int)) (listPair 1000)+ , bench "map/map 1000" $ nf (bimap (map (+1)) (map (*2))) (listPair 1000)+ ]+ ]++-- Test data generators+listPair :: Int -> ([Int], [Int])+listPair n = ([1..n], [1..n])++vectorPair :: Int -> (V.Vector Int, V.Vector Int)+vectorPair n = (V.enumFromN 1 n, V.enumFromN 1 n)++nePair :: Int -> (NonEmpty Int, NonEmpty Int)+nePair n = (1 :| [2..n], 1 :| [2..n])++pairList :: Int -> [(Int, Int)]+pairList n = [(i, i) | i <- [1..n]]++alignedList :: Int -> This [] NonEmpty Int Int+alignedList n = align [1..n] [1..n]++alignedVector :: Int -> This V.Vector NonEmpty Int Int+alignedVector n = align (V.enumFromN 1 n) (V.enumFromN 1 n)
changelog.md view
@@ -1,3 +1,34 @@+0.2.0.0 (2026-05-19)++* Add `Unalign` type class for recovering original functors from alignment+* Add `aligned` isomorphism to Unalign, witnessing that alignment is lossless+* Add `unaligned` isomorphism, the inverse of `aligned`+* Add `unalignWith` method to Unalign, the dual of `alignWith` for transformation during unalignment+* Add `Unalign` instances for Identity, [], Maybe, NonEmpty, ZipList, Seq, Vector+* Add `NFData` instance for This (enables criterion benchmarking)+* Note: Map and IntMap deliberately excluded from Unalign to maintain law compliance+* Add comprehensive law documentation for Semialign, Align, and Unalign type classes+* Add testable law-checking functions: `semialignNaturality`, `semialignSymmetry`,+ `semialignCoherence`, `semialignWithLaw`, `alignRightIdentity`, `alignLeftIdentity`,+ `alignEmpty`, `unalignRoundtrip`, `unalignNaturality`+* Add 12 RULES pragmas for fusion optimization:+ - Semialign fusion: `semialign/naturality`, `alignWith/bimap`, `align/swap/symmetry`, `fmap/as/bimap`+ - Unalign fusion: `unalign/align/roundtrip`, `unalign/bimap/naturality`, `unalignWith/align`+ - Functor/Bifunctor composition: `fmap/fmap/This`, `bimap/bimap/This`+ - Swap optimization: `swap/swap/This`, `swap/bimap/This`, `bimap/swap/This`+* Add 56 INLINE/INLINABLE pragmas for comprehensive optimization:+ - All type class method defaults marked INLINE+ - All Semialign instances marked INLINE or INLINABLE+ - All Align nil methods marked INLINE+ - All Unalign instances marked INLINE or INLINABLE+ - All lens/traversal/fold functions marked INLINE or INLINABLE+ - Strategic use of INLINE for small wrappers, INLINABLE for recursive/larger functions+* Add FUSION.md documenting fusion rules and performance characteristics+* Add comprehensive criterion benchmark suite comparing to zip/unzip+* Reorganize all Semialign instances to be consecutive for better code navigation+* Expand README with examples, comparisons, and usage guide+* Improve cabal file description+ 0.1.0.6 * Fix URLs in cabal file
src/Data/Alignment.hs view
@@ -1,681 +1,1619 @@-{-# OPTIONS_GHC -Wall #-}-{-# LANGUAGE NoImplicitPrelude #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE FlexibleInstances #-}-{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}-{-# HLINT ignore "Use fmap" #-}--module Data.Alignment(--- * Types- This(..)--- * Type-classes-, Semialign(..)-, Align(..)--- * Optics-, these-, those-, allThese-, allThese1-, allThese2-, allThose-, allThoseA-, allThoseAOr-, allThoseB-, allThoseBOr-, allTheseThoseA-, allTheseThoseA1-, allTheseThoseB-, allTheseThoseB1-) where--import Control.Applicative- ( Applicative(liftA2, pure, (<*>)), (<$>), ZipList(ZipList) )-import Control.Category ( Category((.), id) )-import Control.Lens- ( Identity(Identity),- _Just,- _Left,- _Right,- over,- Field1(_1),- Field2(_2),- Lens,- Lens',- Traversal,- Traversal',- Traversal1 )-import Data.Bifoldable ( Bifoldable(bifoldMap) )-import Data.Bifunctor ( Bifunctor(bimap) )-import Data.Bifunctor.Swap ( Swap(..) )-import Data.Bitraversable ( Bitraversable(..) )-import Data.List.NonEmpty ( NonEmpty(..) )-import Data.Bool ( (&&) )-import Data.Either ( Either(..), either )-import Data.Eq ( Eq((==)) )-import Data.Foldable ( Foldable(foldMap) )-import Data.Functor ( Functor(fmap), (<$) )-import Data.Functor.Apply ( Apply((<.>), liftF2) )-import Data.Functor.Classes- ( compare1,- eq1,- showsPrec1,- showsUnaryWith,- Eq1(..),- Ord1(..),- Show1(..) )-import qualified Data.List.NonEmpty as NonEmpty(cons, toList)-import Data.Maybe ( Maybe(..), maybe )-import Data.Monoid ( (<>), Monoid(mempty) )-import Data.Ord ( Ord(compare) )-import Data.Semigroup ( Semigroup )-import Data.Semigroup.Bifoldable ( Bifoldable1(bifoldMap1) )-import Data.Semigroup.Bitraversable ( Bitraversable1(bitraverse1) )-import Data.Semigroup.Foldable ( Foldable1(foldMap1) )-import Data.Semigroup.Traversable ( Traversable1(traverse1) )-import Data.Traversable ( Traversable(traverse) )-import GHC.Show ( Show(showsPrec) )---- $setup--- >>> import Prelude--data This f a b =- This- (f (a, b))- (Maybe (Either (NonEmpty a) (NonEmpty b)))--instance (Eq1 f, Eq a, Eq b) => Eq (This f a b) where- This t1 r1 == This t2 r2 =- t1 `eq1` t2 && r1 == r2--instance (Eq1 f, Eq a) => Eq1 (This f a) where- liftEq f (This t1 r1) (This t2 r2) =- liftEq (liftEq f) t1 t2 && liftEq (liftEq (liftEq f)) r1 r2--instance (Ord1 f, Ord a, Ord b) => Ord (This f a b) where- This t1 r1 `compare` This t2 r2 =- t1 `compare1` t2 <> r1 `compare` r2--instance (Ord1 f, Ord a) => Ord1 (This f a) where- liftCompare f (This t1 r1) (This t2 r2) =- liftCompare (liftCompare f) t1 t2 <> liftCompare (liftCompare (liftCompare f)) r1 r2--instance (Show1 f, Show a, Show b) => Show (This f a b) where- showsPrec d (This t r) =- showsUnaryWith showsPrec1 "This" d t . (" " <>) . showsPrec1 d r--instance (Show1 f, Show a) => Show1 (This f a) where- liftShowsPrec sp sl d (This t r) =- let showsPrecFt = liftShowsPrec (liftShowsPrec sp sl) (liftShowList sp sl)- showsPrecFr = liftShowsPrec (liftShowsPrec (liftShowsPrec sp sl) (liftShowList sp sl)) (liftShowList (liftShowsPrec sp sl) (liftShowList sp sl))- in showsUnaryWith showsPrecFt "This" d t . (" " <>) . showsPrecFr d r--instance Functor f => Bifunctor (This f) where- bimap f g (This t r) =- This (fmap (bimap f g) t) (fmap (bimap (fmap f) (fmap g)) r)--instance Foldable f => Bifoldable (This f) where- bifoldMap f g (This t r) =- foldMap (bifoldMap f g) t <> foldMap (bifoldMap (foldMap f) (foldMap g)) r--instance Foldable1 f => Bifoldable1 (This f) where- bifoldMap1 f g (This t r) =- let x =- foldMap1 (bifoldMap1 f g) t- in maybe x (either (foldMap1 f) (foldMap1 g)) r--instance Traversable f => Bitraversable (This f) where- bitraverse f g (This t r) =- This <$> traverse (bitraverse f g) t <*> traverse (bitraverse (traverse f) (traverse g)) r--instance Traversable1 f => Bitraversable1 (This f) where- bitraverse1 f g (This t r) =- let x =- This <$> traverse1 (bitraverse1 f g) t- in maybe- ((\k -> k Nothing) <$> x)- (\q -> x <.> either (fmap (Just . Left) . traverse1 f) (fmap (Just . Right) . traverse1 g) q)- r--instance Functor f => Functor (This f a) where- fmap =- bimap id---- |------ >>> This [("a", id), ("c", id)] Nothing <.> This [("A", "B"), ("C", "D")] Nothing--- This [("aA","B"),("aC","D"),("cA","B"),("cC","D")] Nothing--- >>> This [("a", id), ("c", id)] Nothing <.> This [("A", "B"), ("C", "D")] (Just (Left ("x":|[])))--- This [("aA","B"),("aC","D"),("cA","B"),("cC","D")] Nothing--- >>> This [("abc", reverse), ("cde", reverse)] Nothing <.> This [("ABC", "DEF"), ("GHI", "JKL")] Nothing--- This [("abcABC","FED"),("abcGHI","LKJ"),("cdeABC","FED"),("cdeGHI","LKJ")] Nothing--- >>> This [("abc", reverse), ("cde", reverse)] Nothing <.> This [("ABC", "DEF"), ("GHI", "JKL")] (Just (Left ("xyz":|[])))--- This [("abcABC","FED"),("abcGHI","LKJ"),("cdeABC","FED"),("cdeGHI","LKJ")] Nothing--- >>> This [("abc", reverse), ("cde", reverse)] Nothing <.> This [("ABC", "DEF"), ("GHI", "JKL")] (Just (Right ("xyz":|[])))--- This [("abcABC","FED"),("abcGHI","LKJ"),("cdeABC","FED"),("cdeGHI","LKJ")] Nothing--- >>> This [("abc", reverse), ("cde", reverse)] (Just (Left ("stu":|[]))) <.> This [("ABC", "DEF"), ("GHI", "JKL")] Nothing--- This [("abcABC","FED"),("abcGHI","LKJ"),("cdeABC","FED"),("cdeGHI","LKJ")] Nothing--- >>> This [("abc", reverse), ("cde", reverse)] (Just (Right (id:|[reverse]))) <.> This [("ABC", "DEF"), ("GHI", "JKL")] Nothing--- This [("abcABC","FED"),("abcGHI","LKJ"),("cdeABC","FED"),("cdeGHI","LKJ")] Nothing--- >>> This [("abc", reverse), ("cde", reverse)] (Just (Left ("stu":|[]))) <.> This [("ABC", "DEF"), ("GHI", "JKL")] (Just (Left ("xyz":|[])))--- This [("abcABC","FED"),("abcGHI","LKJ"),("cdeABC","FED"),("cdeGHI","LKJ")] Just (Left ("stu" :| []))--- >>> This [("abc", reverse), ("cde", reverse)] (Just (Left ("stu":|[]))) <.> This [("ABC", "DEF"), ("GHI", "JKL")] (Just (Right ("xyz":|[])))--- This [("abcABC","FED"),("abcGHI","LKJ"),("cdeABC","FED"),("cdeGHI","LKJ")] Just (Left ("stu" :| []))--- >>> This [("abc", reverse), ("cde", reverse)] (Just (Right (id:|[reverse]))) <.> This [("ABC", "DEF"), ("GHI", "JKL")] (Just (Left ("xyz":|[])))--- This [("abcABC","FED"),("abcGHI","LKJ"),("cdeABC","FED"),("cdeGHI","LKJ")] Just (Left ("xyz" :| []))--- >>> This [("abc", reverse), ("cde", reverse)] (Just (Left ("stu":|[]))) <.> This [("ABC", "DEF"), ("GHI", "JKL")] (Just (Right ("xyz":|[])))--- This [("abcABC","FED"),("abcGHI","LKJ"),("cdeABC","FED"),("cdeGHI","LKJ")] Just (Left ("stu" :| []))-instance (Semigroup a, Apply f) => Apply (This f a) where- This t1 r1 <.> This t2 r2 =- This (liftF2 (<.>) t1 t2) (liftF2 (liftF2 (<.>)) r1 r2)--instance (Monoid a, Applicative f) => Applicative (This f a) where- pure a =- This (pure (mempty, a)) (pure (pure (pure a)))- This t1 r1 <*> This t2 r2 =- This (liftA2 (<*>) t1 t2) (liftF2 (liftF2 (<*>)) r1 r2)---- |------ >>> swap (This [("abc", 'x'), ("def", 'y')] Nothing)--- This [('x',"abc"),('y',"def")] Nothing--- >>> swap (This [("abc", 'x'), ("def", 'y')] (Just (Left ("a":|[]))))--- This [('x',"abc"),('y',"def")] Just (Right ("a" :| []))--- >>> swap (This [("abc", 'x'), ("def", 'y')] (Just (Right ('a':|[]))))--- This [('x',"abc"),('y',"def")] Just (Left ('a' :| ""))-instance Functor f => Swap (This f) where- swap (This t r) =- This (fmap swap t) (fmap swap r)--class Functor f => Semialign f where- align ::- f a- -> f b- -> This f a b- align =- alignWith id id id- alignWith ::- ((a, b) -> (c, d))- -> (a -> c)- -> (b -> d)- -> f a- -> f b- -> This f c d- alignWith f g h t1 t2 =- case align t1 t2 of- This t r ->- This (fmap f t) (fmap (bimap (fmap g) (fmap h)) r)- {-# MINIMAL align | alignWith #-}- alignWith' ::- (a -> c)- -> (b -> d)- -> f a- -> f b- -> This f c d- alignWith' f g =- alignWith (bimap f g) f g---- |------ >>> align "abc" "def"--- This [('a','d'),('b','e'),('c','f')] Nothing--- >>> align "abc" "defghi"--- This [('a','d'),('b','e'),('c','f')] Just (Right ('g' :| "hi"))--- >>> align "abcdef" "ghi"--- This [('a','g'),('b','h'),('c','i')] Just (Left ('d' :| "ef"))-instance Semialign [] where- align (a:as) (b:bs) =- let This t r = align as bs- in This ((a,b):t) r- align (a:as) [] =- This [] (Just (Left (a :| as)))- align [] (b:bs) =- This [] (Just (Right (b :| bs)))- align [] [] =- This [] Nothing---- |------ >>> align (Just "x") (Just "y")--- This (Just ("x","y")) Nothing--- >>> align (Just "x") (Nothing :: Maybe String)--- This Nothing Just (Left ("x" :| []))--- >>> align (Nothing :: Maybe String) (Just "y")--- This Nothing Just (Right ("y" :| []))-instance Semialign Maybe where- align (Just a) (Just b) =- This (Just (a, b)) Nothing- align (Just a) Nothing =- This Nothing (Just (Left (a :| [])))- align Nothing (Just b) =- This Nothing (Just (Right (b :| [])))- align Nothing Nothing =- This Nothing Nothing---- |------ >>> align (Identity "x") (Identity "y")--- This (Identity ("x","y")) Nothing-instance Semialign Identity where- align (Identity a) (Identity b) =- This (Identity (a, b)) Nothing---- |------ >>> align ('a':|"bc") ('g':|"hi")--- This (('a','g') :| [('b','h'),('c','i')]) Nothing--- >>> align ('a':|"bc") ('g':|"hijkl")--- This (('a','g') :| [('b','h'),('c','i')]) Just (Right ('j' :| "kl"))--- >>> align ('a':|"bcdef") ('g':|"hi")--- This (('a','g') :| [('b','h'),('c','i')]) Just (Left ('d' :| "ef"))-instance Semialign NonEmpty where- align (h1:|[]) (h2:|[]) =- This ((h1, h2):|[]) Nothing- align (h1:|i1:r1) (h2:|[]) =- This ((h1, h2):|[]) (Just (Left (i1:|r1)))- align (h1:|[]) (h2:|i2:r2) =- This ((h1, h2):|[]) (Just (Right (i2:|r2)))- align (h1:|i1:r1) (h2:|i2:r2) =- let This t r = align (i1:|r1) (i2:|r2)- in This ((h1, h2) `NonEmpty.cons` t) r--instance Semialign ZipList where- align (ZipList a) (ZipList b) =- over these ZipList (align a b)--class Semialign f => Align f where- nil ::- f a--instance Align [] where- nil =- []--instance Align Maybe where- nil =- Nothing--instance Align ZipList where- nil =- ZipList []---- |------ >>> This [("abc", 's'), ("def", 't')] Nothing <> This [("ghi", 'u'), ("jkl", 'v')] Nothing--- This [("abc",'s'),("def",'t'),("ghi",'u'),("jkl",'v')] Nothing--- >>> This [("abc", 's'), ("def", 't')] Nothing <> This [("ghi", 'u'), ("jkl", 'v')] (Just (Left ("mno":|["pqr"])))--- This [("abc",'s'),("def",'t'),("ghi",'u'),("jkl",'v')] Just (Left ("mno" :| ["pqr"]))--- >>> This [("abc", 's'), ("def", 't')] Nothing <> This [("ghi", 'u'), ("jkl", 'v')] (Just (Right ('o':|"pqr")))--- This [("abc",'s'),("def",'t'),("ghi",'u'),("jkl",'v')] Just (Right ('o' :| "pqr"))--- >>> This [("abc", 's'), ("def", 't')] (Just (Left ("mno":|["pqr"]))) <> This [("ghi", 'u'), ("jkl", 'v')] Nothing--- This [("abc",'s'),("def",'t'),("ghi",'u'),("jkl",'v')] Just (Left ("mno" :| ["pqr"]))--- >>> This [("abc", 's'), ("def", 't')] (Just (Right ('o':|"pqr"))) <> This [("ghi", 'u'), ("jkl", 'v')] Nothing--- This [("abc",'s'),("def",'t'),("ghi",'u'),("jkl",'v')] Just (Right ('o' :| "pqr"))--- >>> This [("abc", 's'), ("def", 't')] (Just (Left ("mno":|["pqr"]))) <> This [("ghi", 'u'), ("jkl", 'v')] (Just (Left ("ccddee":|["ffgghh"])))--- This [("abc",'s'),("def",'t'),("ghi",'u'),("jkl",'v')] Just (Left ("mno" :| ["pqr","ccddee","ffgghh"]))--- >>> This [("abc", 's'), ("def", 't')] (Just (Left ("mno":|["pqr"]))) <> This [("ghi", 'u'), ("jkl", 'v')] (Just (Right ('c':|"ddeeff")))--- This [("abc",'s'),("def",'t'),("ghi",'u'),("jkl",'v'),("mno",'c'),("pqr",'d')] Just (Right ('d' :| "eeff"))--- >>> This [("abc", 's'), ("def", 't')] (Just (Right ('x':|"yyzz"))) <> This [("ghi", 'u'), ("jkl", 'v')] (Just (Right ('c':|"ddeeff")))--- This [("abc",'s'),("def",'t'),("ghi",'u'),("jkl",'v')] Just (Right ('x' :| "yyzzcddeeff"))--- >>> This [("abc", 's'), ("def", 't')] (Just (Right ('x':|"yyzz"))) <> This [("ghi", 'u'), ("jkl", 'v')] (Just (Left ("cc":|["ddeeff"])))--- This [("abc",'s'),("def",'t'),("ghi",'u'),("jkl",'v'),("cc",'x'),("ddeeff",'y')] Just (Right ('y' :| "zz"))-instance Semigroup (This [] a b) where- This t1 (Just (Left as1)) <> This t2 (Just (Left as2)) =- This (t1 <> t2) (Just (Left (as1 <> as2)))- This t1 (Just (Left as1)) <> This t2 (Just (Right bs2)) =- over these (\x -> t1 <> t2 <> NonEmpty.toList x) (align as1 bs2)- This t1 (Just (Left as1)) <> This t2 Nothing =- This (t1 <> t2) (Just (Left as1))- This t1 (Just (Right bs1)) <> This t2 (Just (Right bs2)) =- This (t1 <> t2) (Just (Right (bs1 <> bs2)))- This t1 (Just (Right bs1)) <> This t2 (Just (Left as2)) =- over these (\x -> t1 <> t2 <> NonEmpty.toList x) (align as2 bs1)- This t1 (Just (Right bs1)) <> This t2 Nothing =- This (t1 <> t2) (Just (Right bs1))- This t1 Nothing <> This t2 (Just (Left as2)) =- This (t1 <> t2) (Just (Left as2))- This t1 Nothing <> This t2 (Just (Right bs2)) =- This (t1 <> t2) (Just (Right bs2))- This t1 Nothing <> This t2 Nothing =- This (t1 <> t2) Nothing--instance Semigroup (This NonEmpty a b) where- This t1 (Just (Left as1)) <> This t2 (Just (Left as2)) =- This (t1 <> t2) (Just (Left (as1 <> as2)))- This t1 (Just (Left as1)) <> This t2 (Just (Right bs2)) =- over these (\x -> t1 <> t2 <> x) (align as1 bs2)- This t1 (Just (Left as1)) <> This t2 Nothing =- This (t1 <> t2) (Just (Left as1))- This t1 (Just (Right bs1)) <> This t2 (Just (Right bs2)) =- This (t1 <> t2) (Just (Right (bs1 <> bs2)))- This t1 (Just (Right bs1)) <> This t2 (Just (Left as2)) =- over these (\x -> t1 <> t2 <> x) (align as2 bs1)- This t1 (Just (Right bs1)) <> This t2 Nothing =- This (t1 <> t2) (Just (Right bs1))- This t1 Nothing <> This t2 (Just (Left as2)) =- This (t1 <> t2) (Just (Left as2))- This t1 Nothing <> This t2 (Just (Right bs2)) =- This (t1 <> t2) (Just (Right bs2))- This t1 Nothing <> This t2 Nothing =- This (t1 <> t2) Nothing--instance Monoid (This [] a b) where- mempty =- This mempty Nothing---- |------ >>> over these reverse (This [("abc", 'x'), ("def", 'y')] Nothing)--- This [("def",'y'),("abc",'x')] Nothing--- >>> over these reverse (This [("abc", 'x'), ("def", 'y')] (Just (Left ("ghi":|["jkl"]))))--- This [("def",'y'),("abc",'x')] Just (Left ("ghi" :| ["jkl"]))-these ::- Lens- (This f a b)- (This f' a b)- (f (a, b))- (f' (a, b))-these f (This t r) =- fmap (`This` r) (f t)---- |------ >>> over those (fmap (bimap (fmap reverse) (fmap Data.Char.toUpper))) (This [("abc", 'x'), ("def", 'y')] Nothing)--- This [("abc",'x'),("def",'y')] Nothing--- >>> over those (fmap (bimap (fmap reverse) (fmap Data.Char.toUpper))) (This [("abc", 'x'), ("def", 'y')] (Just (Left ("abc":|["def"]))))--- This [("abc",'x'),("def",'y')] Just (Left ("cba" :| ["fed"]))--- >>> over those (fmap (bimap (fmap reverse) (fmap Data.Char.toUpper))) (This [("abc", 'x'), ("def", 'y')] (Just (Right ('a':|"bcde"))))--- This [("abc",'x'),("def",'y')] Just (Right ('A' :| "BCDE"))--- >>> Control.Lens.view those (This [("abc", 'x'), ("def", 'y')] Nothing)--- Nothing--- >>> Control.Lens.view those (This [("abc", 'x'), ("def", 'y')] (Just (Left ("abc":|["def"]))))--- Just (Left ("abc" :| ["def"]))--- >>> Control.Lens.view those (This [("abc", 'x'), ("def", 'y')] (Just (Right ('a':|"bcde"))))--- Just (Right ('a' :| "bcde"))-those ::- Lens'- (This f a b)- (Maybe (Either (NonEmpty a) (NonEmpty b)))-those f (This t r) =- fmap (This t) (f r)---- |------ >>> over allThese (bimap reverse Data.Char.toUpper) (This [("abc", 'x'), ("def", 'y')] Nothing)--- This [("cba",'X'),("fed",'Y')] Nothing--- >>> over allThese (bimap reverse Data.Char.toUpper) (This [("abc", 'x'), ("def", 'y')] (Just (Left ("abc":|["def"]))))--- This [("cba",'X'),("fed",'Y')] Just (Left ("abc" :| ["def"]))--- >>> over allThese (bimap reverse Data.Char.toUpper) (This [("abc", 'x'), ("def", 'y')] (Just (Right ('a':|"bcde"))))--- This [("cba",'X'),("fed",'Y')] Just (Right ('a' :| "bcde"))--- >>> Control.Lens.preview allThese (This [("abc", 'x'), ("def", 'y')] Nothing)--- Just ("abc",'x')--- >>> Control.Lens.preview allThese (This [("abc", 'x'), ("def", 'y')] (Just (Left ("abc":|["def"]))))--- Just ("abc",'x')--- >>> Control.Lens.preview allThese (This [("abc", 'x'), ("def", 'y')] (Just (Right ('a':|"bcde"))))--- Just ("abc",'x')-allThese ::- Traversable f =>- Traversal'- (This f a b)- (a, b)-allThese =- these . traverse---- |------ >>> over allThese1 reverse (This [("abc", 'x'), ("def", 'y')] Nothing)--- This [("cba",'x'),("fed",'y')] Nothing--- >>> over allThese1 reverse (This [("abc", 'x'), ("def", 'y')] (Just (Left ("abc":|["def"]))))--- This [("cba",'x'),("fed",'y')] Just (Left ("abc" :| ["def"]))--- >>> over allThese1 reverse (This [("abc", 'x'), ("def", 'y')] (Just (Right ('a':|"bcde"))))--- This [("cba",'x'),("fed",'y')] Just (Right ('a' :| "bcde"))--- >>> Control.Lens.preview allThese1 (This [("abc", 'x'), ("def", 'y')] Nothing)--- Just "abc"--- >>> Control.Lens.preview allThese1 (This [("abc", 'x'), ("def", 'y')] (Just (Left ("abc":|["def"]))))--- Just "abc"--- >>> Control.Lens.preview allThese1 (This [("abc", 'x'), ("def", 'y')] (Just (Right ('a':|"bcde"))))--- Just "abc"-allThese1 ::- Traversable f =>- Traversal'- (This f a b)- a-allThese1 =- allThese . _1---- |------ >>> over allThese2 Data.Char.toUpper (This [("abc", 'x'), ("def", 'y')] Nothing)--- This [("abc",'X'),("def",'Y')] Nothing--- >>> over allThese2 Data.Char.toUpper (This [("abc", 'x'), ("def", 'y')] (Just (Left ("abc":|["def"]))))--- This [("abc",'X'),("def",'Y')] Just (Left ("abc" :| ["def"]))--- >>> over allThese2 Data.Char.toUpper (This [("abc", 'x'), ("def", 'y')] (Just (Right ('a':|"bcde"))))--- This [("abc",'X'),("def",'Y')] Just (Right ('a' :| "bcde"))--- >>> Control.Lens.preview allThese2 (This [("abc", 'x'), ("def", 'y')] Nothing)--- Just 'x'--- >>> Control.Lens.preview allThese2 (This [("abc", 'x'), ("def", 'y')] (Just (Left ("abc":|["def"]))))--- Just 'x'--- >>> Control.Lens.preview allThese2 (This [("abc", 'x'), ("def", 'y')] (Just (Right ('a':|"bcde"))))--- Just 'x'-allThese2 ::- Traversable f =>- Traversal'- (This f a b)- b-allThese2 =- allThese . _2---- |------ >>> over allThose (bimap (fmap reverse) (fmap Data.Char.toUpper)) (This [("abc", 'x'), ("def", 'y')] Nothing)--- This [("abc",'x'),("def",'y')] Nothing--- >>> over allThose (bimap (fmap reverse) (fmap Data.Char.toUpper)) (This [("abc", 'x'), ("def", 'y')] (Just (Left ("abc":|["def"]))))--- This [("abc",'x'),("def",'y')] Just (Left ("cba" :| ["fed"]))--- >>> over allThose (bimap (fmap reverse) (fmap Data.Char.toUpper)) (This [("abc", 'x'), ("def", 'y')] (Just (Right ('a':|"bcde"))))--- This [("abc",'x'),("def",'y')] Just (Right ('A' :| "BCDE"))--- >>> Control.Lens.preview allThose (This [("abc", 'x'), ("def", 'y')] Nothing)--- Nothing--- >>> Control.Lens.preview allThose (This [("abc", 'x'), ("def", 'y')] (Just (Left ("abc":|["def"]))))--- Just (Left ("abc" :| ["def"]))--- >>> Control.Lens.preview allThose (This [("abc", 'x'), ("def", 'y')] (Just (Right ('a':|"bcde"))))--- Just (Right ('a' :| "bcde"))-allThose ::- Traversal'- (This f a b)- (Either (NonEmpty a) (NonEmpty b))-allThose =- those . _Just---- |------ >>> over allThoseA (fmap reverse) (This [("abc", 'x'), ("def", 'y')] Nothing)--- This [("abc",'x'),("def",'y')] Nothing--- >>> over allThoseA (fmap reverse) (This [("abc", 'x'), ("def", 'y')] (Just (Left ("abc":|["def"]))))--- This [("abc",'x'),("def",'y')] Just (Left ("cba" :| ["fed"]))--- >>> over allThoseA (fmap reverse) (This [("abc", 'x'), ("def", 'y')] (Just (Right ('a':|"bcde"))))--- This [("abc",'x'),("def",'y')] Just (Right ('a' :| "bcde"))--- >>> Control.Lens.preview allThoseA (This [("abc", 'x'), ("def", 'y')] Nothing)--- Nothing--- >>> Control.Lens.preview allThoseA (This [("abc", 'x'), ("def", 'y')] (Just (Left ("abc":|["def"]))))--- Just ("abc" :| ["def"])--- >>> Control.Lens.preview allThoseA (This [("abc", 'x'), ("def", 'y')] (Just (Right ('a':|"bcde"))))--- Nothing-allThoseA ::- Traversal'- (This f a b)- (NonEmpty a)-allThoseA =- allThose . _Left---- |------ >>> over allThoseAOr reverse (This [("abc", 'x'), ("def", 'y')] Nothing)--- This [("abc",'x'),("def",'y')] Nothing--- >>> over allThoseAOr reverse (This [("abc", 'x'), ("def", 'y')] (Just (Left ("abc":|["def"]))))--- This [("abc",'x'),("def",'y')] Just (Left ("def" :| ["abc"]))--- >>> over allThoseAOr reverse (This [("abc", 'x'), ("def", 'y')] (Just (Right ('a':|"bcde"))))--- This [("abc",'x'),("def",'y')] Just (Right ('a' :| "bcde"))--- >>> Control.Lens.preview allThoseAOr (This [("abc", 'x'), ("def", 'y')] Nothing)--- Just []--- >>> Control.Lens.preview allThoseAOr (This [("abc", 'x'), ("def", 'y')] (Just (Left ("abc":|["def"]))))--- Just ["abc","def"]--- >>> Control.Lens.preview allThoseAOr (This [("abc", 'x'), ("def", 'y')] (Just (Right ('a':|"bcde"))))--- Nothing-allThoseAOr ::- Traversal'- (This f a b)- [a]-allThoseAOr f (This t Nothing) =- This t <$> (Nothing <$ f [])-allThoseAOr _ th@(This _ (Just (Right _))) =- pure th-allThoseAOr f (This t (Just (Left a))) =- let lst [] = Nothing- lst (x:y) = Just (x:|y)- in This t . fmap Left . lst <$> f (NonEmpty.toList a)---- |------ >>> over allThoseB (fmap Data.Char.toUpper) (This [("abc", 'x'), ("def", 'y')] Nothing)--- This [("abc",'x'),("def",'y')] Nothing--- >>> over allThoseB (fmap Data.Char.toUpper) (This [("abc", 'x'), ("def", 'y')] (Just (Left ("abc":|["def"]))))--- This [("abc",'x'),("def",'y')] Just (Left ("abc" :| ["def"]))--- >>> over allThoseB (fmap Data.Char.toUpper) (This [("abc", 'x'), ("def", 'y')] (Just (Right ('a':|"bcde"))))--- This [("abc",'x'),("def",'y')] Just (Right ('A' :| "BCDE"))--- >>> Control.Lens.preview allThoseB (This [("abc", 'x'), ("def", 'y')] Nothing)--- Nothing--- >>> Control.Lens.preview allThoseB (This [("abc", 'x'), ("def", 'y')] (Just (Left ("abc":|["def"]))))--- Nothing--- >>> Control.Lens.preview allThoseB (This [("abc", 'x'), ("def", 'y')] (Just (Right ('a':|"bcde"))))--- Just ('a' :| "bcde")-allThoseB ::- Traversal'- (This f a b)- (NonEmpty b)-allThoseB =- allThose . _Right---- |------ >>> over allThoseBOr reverse (This [("abc", 'x'), ("def", 'y')] Nothing)--- This [("abc",'x'),("def",'y')] Nothing--- >>> over allThoseBOr reverse (This [("abc", 'x'), ("def", 'y')] (Just (Left ("abc":|["def"]))))--- This [("abc",'x'),("def",'y')] Just (Left ("abc" :| ["def"]))--- >>> over allThoseBOr reverse (This [("abc", 'x'), ("def", 'y')] (Just (Right ('a':|"bcde"))))--- This [("abc",'x'),("def",'y')] Just (Right ('e' :| "dcba"))--- >>> Control.Lens.preview allThoseBOr (This [("abc", 'x'), ("def", 'y')] Nothing)--- Just ""--- >>> Control.Lens.preview allThoseBOr (This [("abc", 'x'), ("def", 'y')] (Just (Left ("abc":|["def"]))))--- Nothing--- >>> Control.Lens.preview allThoseBOr (This [("abc", 'x'), ("def", 'y')] (Just (Right ('a':|"bcde"))))--- Just "abcde"-allThoseBOr ::- Traversal'- (This f a b)- [b]-allThoseBOr f (This t Nothing) =- This t <$> (Nothing <$ f [])-allThoseBOr f (This t (Just (Right b))) =- let lst [] = Nothing- lst (x:y) = Just (x:|y)- in This t . fmap Right . lst <$> f (NonEmpty.toList b)-allThoseBOr _ th@(This _ (Just (Left _))) =- pure th---- |------ >>> over allTheseThoseA (fmap Data.Char.toUpper) (This [("abc", 'x'), ("def", 'y')] Nothing)--- This [("ABC",'x'),("DEF",'y')] Nothing--- >>> over allTheseThoseA (fmap Data.Char.toUpper) (This [("abc", 'x'), ("def", 'y')] (Just (Left ("abc":|["def"]))))--- This [("ABC",'x'),("DEF",'y')] Just (Left ("ABC" :| ["DEF"]))--- >>> over allTheseThoseA (fmap Data.Char.toUpper) (This [("abc", 'x'), ("def", 'y')] (Just (Right ('a':|"bcde"))))--- This [("ABC",'x'),("DEF",'y')] Just (Right ('a' :| "bcde"))--- >>> Control.Lens.preview allTheseThoseA (This [("abc", 'x'), ("def", 'y')] Nothing)--- Just "abc"--- >>> Control.Lens.preview allTheseThoseA (This [("abc", 'x'), ("def", 'y')] (Just (Left ("abc":|["def"]))))--- Just "abc"--- >>> Control.Lens.preview allTheseThoseA (This [("abc", 'x'), ("def", 'y')] (Just (Right ('a':|"bcde"))))--- Just "abc"-allTheseThoseA ::- Traversable f =>- Traversal- (This f a b)- (This f a' b)- a- a'-allTheseThoseA f (This t r) =- let th =- case r of- Nothing ->- pure Nothing- Just (Left as) ->- Just . Left <$> traverse f as- Just (Right bs) ->- pure (Just (Right bs))- in This <$>- traverse (\(a, b) -> (, b) <$> f a) t <*> th--allTheseThoseA1 ::- Traversable1 f =>- Traversal1- (This f a b)- (This f a' b)- a- a'-allTheseThoseA1 f (This t r) =- let x = This <$> traverse1 (\(a, b) -> (, b) <$> f a) t- in maybe- ((\k -> k Nothing) <$> x)- (- either- ((\p -> (\k -> k . Just . Left) <$> x <.> p) . traverse1 f)- (\z -> (\k -> k (Just (Right z))) <$> x))- r---- |------ >>> over allTheseThoseB Data.Char.toUpper (This [("abc", 'x'), ("def", 'y')] Nothing)--- This [("abc",'X'),("def",'Y')] Nothing--- >>> over allTheseThoseB Data.Char.toUpper (This [("abc", 'x'), ("def", 'y')] (Just (Left ("abc":|["def"]))))--- This [("abc",'X'),("def",'Y')] Just (Left ("abc" :| ["def"]))--- >>> over allTheseThoseB Data.Char.toUpper (This [("abc", 'x'), ("def", 'y')] (Just (Right ('a':|"bcde"))))--- This [("abc",'X'),("def",'Y')] Just (Right ('A' :| "BCDE"))--- >>> Control.Lens.preview allTheseThoseB (This [("abc", 'x'), ("def", 'y')] Nothing)--- Just 'x'--- >>> Control.Lens.preview allTheseThoseB (This [("abc", 'x'), ("def", 'y')] (Just (Left ("abc":|["def"]))))--- Just 'x'--- >>> Control.Lens.preview allTheseThoseB (This [("abc", 'x'), ("def", 'y')] (Just (Right ('a':|"bcde"))))--- Just 'x'-allTheseThoseB ::- Traversable f =>- Traversal- (This f a b)- (This f a b')- b- b'-allTheseThoseB f (This t r) =- let th =- case r of- Nothing ->- pure Nothing- Just (Left as) ->- pure (Just (Left as))- Just (Right bs) ->- Just . Right <$> traverse f bs- in This <$>- traverse (\(a, b) -> (a ,) <$> f b) t <*> th--allTheseThoseB1 ::- Traversable1 f =>- Traversal1- (This f a b)- (This f a b')- b- b'-allTheseThoseB1 f (This t r) =- let x = This <$> traverse1 (\(a, b) -> (a ,) <$> f b) t- in maybe- ((\k -> k Nothing) <$> x)- (either- (\z -> (\k -> k (Just (Left z))) <$> x)- ((\p -> (\k -> k . Just . Right) <$> x <.> p) . traverse1 f))- r+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# OPTIONS_GHC -Wall #-}+-- Suppress inline-rule-shadowing warnings for fusion RULES.+-- The warnings are benign: our RULES use phase [2] to avoid conflicts,+-- and they work correctly in optimized code where fusion matters.+{-# OPTIONS_GHC -Wno-inline-rule-shadowing #-}++module Data.Alignment+ ( -- * Data type+ This (..),+ This',++ -- * Type classes+ GetThis (..),+ HasThis (..),+ ReviewThis (..),+ AsThis (..),+ Semialign (..),+ Align (..),+ Unalign (..),++ -- * Lenses+ these,+ those,+ thoseLeft,+ thoseRight,++ -- * Traversals+ traverseA,+ traverseB,+ traverseA1,+ traverseB1,++ -- * Folds+ foldA,+ foldB,+ foldA1,+ foldB1,++ -- * Isomorphisms+ unaligned,++ -- * Law-checking functions+ semialignNaturality,+ semialignSymmetry,+ semialignCoherence,+ semialignWithLaw,+ alignRightIdentity,+ alignLeftIdentity,+ alignEmpty,+ unalignRoundtrip,+ unalignNaturality,+ )+where++import Control.Applicative+ ( Applicative (pure, (<*>)),+ ZipList (ZipList, getZipList),+ (<$>),+ (<*),+ )+import Control.Category (Category (id, (.)))+import Control.DeepSeq (NFData (rnf))+import Control.Lens+ ( Fold,+ Fold1,+ Getter,+ Identity (Identity),+ Iso',+ Lens,+ Lens',+ Prism',+ Review,+ Traversal,+ Traversal',+ Traversal1,+ iso,+ lens,+ over,+ prism',+ review,+ unto,+ view,+ _Left,+ _Right,+ )+import Data.Bifoldable (Bifoldable (bifoldMap))+import Data.Bifunctor (Bifunctor (bimap), second)+import Data.Bifunctor.Swap (Swap (..))+import Data.Bitraversable (Bitraversable (..))+import Data.Bool (Bool (False, True), otherwise, (&&))+import Data.Either (Either (..), either)+import Data.Eq (Eq ((==)))+import Data.Foldable (Foldable (foldMap), traverse_)+import Data.Function (const, flip, ($))+import Data.Tuple (uncurry)+import Data.Functor (Functor (fmap), ($>), (<$))+import Data.Functor.Apply (Apply ((<.>)), (.>))+import Data.Functor.Classes+ ( Eq1 (..),+ Eq2 (..),+ Ord1 (..),+ Ord2 (..),+ Show1 (..),+ Show2 (..),+ liftShowList2,+ )+import Data.Functor.Const (Const (Const))+import Data.IntMap (IntMap)+import qualified Data.IntMap as IntMap+import Data.List ((++))+import qualified Data.List as List+import Data.List.NonEmpty (NonEmpty (..))+import qualified Data.List.NonEmpty as NonEmpty (cons)+import Data.Map (Map)+import qualified Data.Map as Map+import Data.Maybe (Maybe (..))+import Data.Monoid (Monoid (mempty), (<>))+import Data.Ord (Ord (compare, min), (>))+import Data.Semigroup (Semigroup)+import Data.Semigroup.Bifoldable (Bifoldable1 (bifoldMap1))+import Data.Semigroup.Bitraversable (Bitraversable1 (bitraverse1))+import Data.Semigroup.Foldable (Foldable1 (foldMap1))+import Data.Semigroup.Traversable (Traversable1 (traverse1))+import Data.Sequence (Seq)+import qualified Data.Sequence as Seq+import Data.Traversable (Traversable (traverse))+import Data.Vector (Vector)+import qualified Data.Vector as Vector+import GHC.Generics (Generic, Generic1)+import GHC.Show (Show (showsPrec))+import Text.Show (showList, showParen, showString)++-- $setup+-- >>> import Prelude+-- >>> import Data.List.NonEmpty (NonEmpty(..))+-- >>> import qualified Data.Sequence as Seq+-- >>> import qualified Data.Vector as Vector+-- >>> import qualified Data.Map as Map+-- >>> import qualified Data.IntMap as IntMap+-- >>> import Data.Functor.Const (Const(..))++-- | Alignment result type combining matched pairs with leftovers+--+-- >>> This [(1,2)] Nothing :: This [] NonEmpty Int Int+-- This [(1,2)] Nothing+--+-- >>> This [(1,2)] (Just (Left (3 :| []))) :: This [] NonEmpty Int Int+-- This [(1,2)] (Just (Left (3 :| [])))+data This f g a b+ = This+ (f (a, b))+ (Maybe (Either (g a) (g b)))+ deriving (Generic, Generic1)++-- | Type alias for This when both functor parameters are the same+--+-- This simplifies the type signature when aligning structures where+-- leftovers on either side use the same container type.+--+-- >>> align (1 :| [2]) (3 :| [4,5]) :: This' NonEmpty Int Int+-- This ((1,3) :| [(2,4)]) (Just (Right (5 :| [])))+-- >>> align (Identity 1) (Identity 2) :: This' Identity Int Int+-- This (Identity (1,2)) Nothing+type This' f a b = This f f a b++-- | GetThis type class - provides a Getter to view a value as This+--+-- >>> import Control.Lens (view)+-- >>> view getThis (This [(1,2)] Nothing :: This [] NonEmpty Int Int)+-- This [(1,2)] Nothing+class GetThis s f g a b | s -> f g a b where+ getThis ::+ Getter s (This f g a b)++instance GetThis (This f g a b) f g a b where+ getThis =+ id+ {-# INLINE getThis #-}++-- | HasThis type class - provides a Lens for This+--+-- >>> import Control.Lens (set)+-- >>> set this' (This [(3,4)] Nothing) (This [(1,2)] Nothing :: This [] NonEmpty Int Int)+-- This [(3,4)] Nothing+class (GetThis s f g a b) => HasThis s f g a b | s -> f g a b where+ {-# MINIMAL setThis #-}+ setThis ::+ This f g a b -> s -> s+ this' ::+ Lens' s (This f g a b)+ this' =+ lens (view getThis) (flip setThis)+ {-# INLINE this' #-}++instance HasThis (This f g a b) f g a b where+ setThis =+ const+ {-# INLINE setThis #-}++-- | ReviewThis type class - provides a Review to construct a value from This+--+-- >>> review reviewThis (This [(1,2)] Nothing) :: This [] NonEmpty Int Int+-- This [(1,2)] Nothing+class ReviewThis s f g a b | s -> f g a b where+ reviewThis ::+ Review s (This f g a b)++instance ReviewThis (This f g a b) f g a b where+ reviewThis =+ unto id+ {-# INLINE reviewThis #-}++-- | AsThis type class - provides a Prism for This+--+-- >>> matchThis (This [(1,2)] Nothing :: This [] NonEmpty Int Int)+-- Just (This [(1,2)] Nothing)+-- >>> import Control.Lens (preview)+-- >>> preview _This (This [(1,2)] Nothing :: This [] NonEmpty Int Int)+-- Just (This [(1,2)] Nothing)+class (ReviewThis s f g a b) => AsThis s f g a b | s -> f g a b where+ {-# MINIMAL matchThis #-}+ matchThis ::+ s -> Maybe (This f g a b)+ _This ::+ Prism' s (This f g a b)+ _This =+ prism' (review reviewThis) matchThis+ {-# INLINE _This #-}++instance AsThis (This f g a b) f g a b where+ matchThis =+ Just+ {-# INLINE matchThis #-}++-- | Eq instance for This when all type parameters are concrete types with Eq+--+-- >>> (This [(1,2)] Nothing :: This [] NonEmpty Int Int) == This [(1,2)] Nothing+-- True+-- >>> (This [(1,2)] Nothing :: This [] NonEmpty Int Int) == This [(1,3)] Nothing+-- False+-- >>> (This [(1,2)] (Just (Left (3 :| []))) :: This [] NonEmpty Int Int) == This [(1,2)] (Just (Left (3 :| [])))+-- True+instance (Eq1 f, Eq1 g, Eq a, Eq b) => Eq (This f g a b) where+ This t1 r1 == This t2 r2 =+ liftEq (==) t1 t2 && liftEq (liftEq2 (liftEq (==)) (liftEq (==))) r1 r2++-- | Eq1 instance - makes the last type parameter (b) polymorphic+--+-- >>> liftEq (==) (This [(1,2)] Nothing) (This [(1,2)] Nothing :: This [] NonEmpty Int Int)+-- True+-- >>> liftEq (==) (This [(1,2)] Nothing) (This [(1,3)] Nothing :: This [] NonEmpty Int Int)+-- False+instance (Eq1 f, Eq1 g, Eq a) => Eq1 (This f g a) where+ liftEq eqB (This t1 r1) (This t2 r2) =+ liftEq (liftEq2 (==) eqB) t1 t2 && liftEq (liftEq2 (liftEq (==)) (liftEq eqB)) r1 r2++-- | Eq2 instance - makes both type parameters (a, b) polymorphic+--+-- >>> liftEq2 (==) (==) (This [(1,2)] Nothing) (This [(1,2)] Nothing :: This [] NonEmpty Int Int)+-- True+-- >>> liftEq2 (==) (==) (This [(1,2)] Nothing) (This [(2,2)] Nothing :: This [] NonEmpty Int Int)+-- False+instance (Eq1 f, Eq1 g) => Eq2 (This f g) where+ liftEq2 eqA eqB (This t1 r1) (This t2 r2) =+ liftEq (liftEq2 eqA eqB) t1 t2 && liftEq (liftEq2 (liftEq eqA) (liftEq eqB)) r1 r2++-- | Ord instance for This when all type parameters are concrete types with Ord+--+-- >>> compare (This [(1,2)] Nothing) (This [(1,2)] Nothing :: This [] NonEmpty Int Int)+-- EQ+-- >>> compare (This [(1,2)] Nothing) (This [(1,3)] Nothing :: This [] NonEmpty Int Int)+-- LT+-- >>> compare (This [(2,2)] Nothing) (This [(1,3)] Nothing :: This [] NonEmpty Int Int)+-- GT+instance (Ord1 f, Ord1 g, Ord a, Ord b) => Ord (This f g a b) where+ compare (This t1 r1) (This t2 r2) =+ liftCompare compare t1 t2 <> liftCompare (liftCompare2 (liftCompare compare) (liftCompare compare)) r1 r2++-- | Ord1 instance - makes the last type parameter (b) polymorphic+--+-- >>> liftCompare compare (This [(1,2)] Nothing) (This [(1,2)] Nothing :: This [] NonEmpty Int Int)+-- EQ+-- >>> liftCompare compare (This [(1,2)] Nothing) (This [(1,3)] Nothing :: This [] NonEmpty Int Int)+-- LT+instance (Ord1 f, Ord1 g, Ord a) => Ord1 (This f g a) where+ liftCompare cmpB (This t1 r1) (This t2 r2) =+ liftCompare (liftCompare2 compare cmpB) t1 t2 <> liftCompare (liftCompare2 (liftCompare compare) (liftCompare cmpB)) r1 r2++-- | Ord2 instance - makes both type parameters (a, b) polymorphic+--+-- >>> liftCompare2 compare compare (This [(1,2)] Nothing) (This [(1,2)] Nothing :: This [] NonEmpty Int Int)+-- EQ+-- >>> liftCompare2 compare compare (This [(1,2)] Nothing) (This [(2,2)] Nothing :: This [] NonEmpty Int Int)+-- LT+instance (Ord1 f, Ord1 g) => Ord2 (This f g) where+ liftCompare2 cmpA cmpB (This t1 r1) (This t2 r2) =+ liftCompare (liftCompare2 cmpA cmpB) t1 t2 <> liftCompare (liftCompare2 (liftCompare cmpA) (liftCompare cmpB)) r1 r2++-- | Show instance for This when all type parameters are concrete types with Show+--+-- >>> This [(1,2)] Nothing :: This [] NonEmpty Int Int+-- This [(1,2)] Nothing+-- >>> This [(1,2)] (Just (Left (3 :| []))) :: This [] NonEmpty Int Int+-- This [(1,2)] (Just (Left (3 :| [])))+instance (Show1 f, Show1 g, Show a, Show b) => Show (This f g a b) where+ showsPrec d (This t r) =+ showParen (d > 10) $+ showString "This "+ . showsPrec 11 t+ . showString " "+ . showsPrec 11 r++-- | Show1 instance - makes the last type parameter (b) polymorphic+instance (Show1 f, Show1 g, Show a) => Show1 (This f g a) where+ liftShowsPrec spB slB d (This t r) =+ showParen (d > 10) $+ showString "This "+ . liftShowsPrec (liftShowsPrec2 showsPrec showList spB slB) (liftShowList2 showsPrec showList spB slB) 11 t+ . showString " "+ . liftShowsPrec (liftShowsPrec2 (liftShowsPrec showsPrec showList) (liftShowList showsPrec showList) (liftShowsPrec spB slB) (liftShowList spB slB)) (liftShowList2 (liftShowsPrec showsPrec showList) (liftShowList showsPrec showList) (liftShowsPrec spB slB) (liftShowList spB slB)) 11 r++-- | Show2 instance - makes both type parameters (a, b) polymorphic+instance (Show1 f, Show1 g) => Show2 (This f g) where+ liftShowsPrec2 spA slA spB slB d (This t r) =+ showParen (d > 10) $+ showString "This "+ . liftShowsPrec (liftShowsPrec2 spA slA spB slB) (liftShowList2 spA slA spB slB) 11 t+ . showString " "+ . liftShowsPrec (liftShowsPrec2 (liftShowsPrec spA slA) (liftShowList spA slA) (liftShowsPrec spB slB) (liftShowList spB slB)) (liftShowList2 (liftShowsPrec spA slA) (liftShowList spA slA) (liftShowsPrec spB slB) (liftShowList spB slB)) 11 r++-- | Functor instance - maps over the second type parameter (b)+-- Maps the function over b in the tuple (a,b) and over b in the Either branch+--+-- >>> fmap (*10) (This [(1,2)] Nothing :: This [] NonEmpty Int Int)+-- This [(1,20)] Nothing+-- >>> fmap (*10) (This [(1,2)] (Just (Right (3 :| []))) :: This [] NonEmpty Int Int)+-- This [(1,20)] (Just (Right (30 :| [])))+-- >>> fmap (*10) (This [(1,2)] (Just (Left (3 :| []))) :: This [] NonEmpty Int Int)+-- This [(1,20)] (Just (Left (3 :| [])))+instance (Functor f, Functor g) => Functor (This f g a) where+ fmap h (This t r) =+ This (fmap (second h) t) (fmap (fmap (fmap h)) r)++-- | Bifunctor instance - operates on both type parameters (a, b)+-- The first function maps a, the second function maps b+--+-- >>> bimap (*10) (*100) (This [(1,2)] Nothing :: This [] NonEmpty Int Int)+-- This [(10,200)] Nothing+-- >>> bimap (*10) (*100) (This [(1,2)] (Just (Left (3 :| []))) :: This [] NonEmpty Int Int)+-- This [(10,200)] (Just (Left (30 :| [])))+-- >>> bimap (*10) (*100) (This [(1,2)] (Just (Right (3 :| []))) :: This [] NonEmpty Int Int)+-- This [(10,200)] (Just (Right (300 :| [])))+instance (Functor f, Functor g) => Bifunctor (This f g) where+ bimap fa fb (This t r) =+ This (fmap (bimap fa fb) t) (fmap (bimap (fmap fa) (fmap fb)) r)++-- | Swap instance - swaps the two type parameters+--+-- >>> swap (This [(1,'a')] Nothing :: This [] NonEmpty Int Char)+-- This [('a',1)] Nothing+-- >>> swap (This [(1,'a')] (Just (Left (3 :| []))) :: This [] NonEmpty Int Char)+-- This [('a',1)] (Just (Right (3 :| [])))+-- >>> swap (This [(1,'a')] (Just (Right ('b' :| ""))) :: This [] NonEmpty Int Char)+-- This [('a',1)] (Just (Left ('b' :| "")))+instance (Functor f) => Swap (This f g) where+ swap (This t r) =+ This (fmap (\(a, b) -> (b, a)) t) (fmap swapEither r)+ where+ swapEither (Left ga) = Right ga+ swapEither (Right gb) = Left gb++-- | NFData instance for strict evaluation in benchmarks+--+-- Forces evaluation of both the paired component and the leftover component.+instance (NFData (f (a, b)), NFData (g a), NFData (g b)) => NFData (This f g a b) where+ rnf (This pairs leftover) = rnf (pairs, leftover)+ {-# INLINE rnf #-}++-- * Functor/Bifunctor/Swap fusion rules+--+-- These rules optimize composition of mapping and swapping operations on This.+-- Phase [2] ensures they fire after instance resolution.++{-# RULES++-- Functor composition on This - reduces to single traversal+"fmap/fmap/This" [2] forall f g (x :: This [] NonEmpty a b).+ fmap f (fmap g x) = fmap (f . g) x++-- Bifunctor composition on This - reduces to single traversal+"bimap/bimap/This" [2] forall f1 f2 g1 g2 (x :: This [] NonEmpty a b).+ bimap f1 g1 (bimap f2 g2 x) = bimap (f1 . f2) (g1 . g2) x++-- Swap involution - swap is its own inverse, complete elimination+"swap/swap/This" [2] forall (x :: This [] NonEmpty a b).+ swap (swap x) = x++-- Swap and bimap commute by swapping function arguments+"swap/bimap/This" [2] forall f g (x :: This [] NonEmpty a b).+ swap (bimap f g x) = bimap g f (swap x)++-- bimap and swap commute (reverse direction)+"bimap/swap/This" [2] forall f g (x :: This [] NonEmpty a b).+ bimap f g (swap x) = swap (bimap g f x)++ #-}++-- | Semigroup instance - combines two This values by combining their components+--+-- >>> This [(1,2)] Nothing <> This [(3,4)] Nothing :: This [] NonEmpty Int Int+-- This [(1,2),(3,4)] Nothing+-- >>> This [(1,2)] (Just (Left (3 :| []))) <> This [(4,5)] (Just (Left (6 :| [])))+-- This [(1,2),(4,5)] (Just (Left (3 :| [6])))+-- >>> This [(1,2)] (Just (Right (3 :| []))) <> This [(4,5)] (Just (Right (6 :| [])))+-- This [(1,2),(4,5)] (Just (Right (3 :| [6])))+-- >>> This [(1,2)] (Just (Left (3 :| []))) <> This [(4,5)] (Just (Right (6 :| [])))+-- This [(1,2),(4,5)] (Just (Right (6 :| [])))+instance (Semigroup (f (a, b)), Semigroup (g a), Semigroup (g b)) => Semigroup (This f g a b) where+ This t1 r1 <> This t2 r2 =+ This (t1 <> t2) (combine r1 r2)+ where+ combine Nothing Nothing = Nothing+ combine (Just x) Nothing = Just x+ combine Nothing (Just y) = Just y+ combine (Just (Left ga1)) (Just (Left ga2)) = Just (Left (ga1 <> ga2))+ combine (Just (Right gb1)) (Just (Right gb2)) = Just (Right (gb1 <> gb2))+ combine (Just (Left _)) (Just (Right gb)) = Just (Right gb)+ combine (Just (Right gb)) (Just (Left _)) = Just (Right gb)++-- | Monoid instance - identity is empty f and Nothing+--+-- >>> mempty :: This [] NonEmpty Int Int+-- This [] Nothing+-- >>> mempty <> This [(1,2)] Nothing :: This [] NonEmpty Int Int+-- This [(1,2)] Nothing+instance (Monoid (f (a, b)), Semigroup (g a), Semigroup (g b)) => Monoid (This f g a b) where+ mempty = This mempty Nothing++-- | Bifoldable instance - folds over both type parameters+--+-- >>> import Data.Monoid (Sum(..))+-- >>> bifoldMap Sum Sum (This [(1,2),(3,4)] Nothing :: This [] NonEmpty Int Int)+-- Sum {getSum = 10}+-- >>> bifoldMap Sum Sum (This [(1,2)] (Just (Left (3 :| []))) :: This [] NonEmpty Int Int)+-- Sum {getSum = 6}+-- >>> bifoldMap Sum Sum (This [(1,2)] (Just (Right (3 :| []))) :: This [] NonEmpty Int Int)+-- Sum {getSum = 6}+instance (Foldable f, Foldable g) => Bifoldable (This f g) where+ bifoldMap ha hb (This t r) =+ foldMap (\(a, b) -> ha a <> hb b) t <> foldMap (either (foldMap ha) (foldMap hb)) r+ {-# INLINE bifoldMap #-}++-- | Foldable instance - folds over b values in tuples and in the Either branch+--+-- >>> import Data.Monoid (Sum(..))+-- >>> foldMap Sum (This [(1,2),(3,4)] Nothing :: This [] NonEmpty Int Int)+-- Sum {getSum = 6}+-- >>> foldMap Sum (This [(1,2)] (Just (Left (3 :| []))) :: This [] NonEmpty Int Int)+-- Sum {getSum = 2}+-- >>> foldMap Sum (This [(1,2)] (Just (Right (3 :| []))) :: This [] NonEmpty Int Int)+-- Sum {getSum = 5}+instance (Foldable f, Foldable g) => Foldable (This f g a) where+ foldMap h (This t r) =+ foldMap (\(_, b) -> h b) t <> foldMap (either (pure mempty) (foldMap h)) r+ {-# INLINE foldMap #-}++-- | Bifoldable1 instance - folds over both type parameters with at least one value+--+-- >>> import Data.Semigroup (Sum(..))+-- >>> bifoldMap1 Sum Sum (This ((1,2) :| [(3,4)]) Nothing :: This NonEmpty NonEmpty Int Int)+-- Sum {getSum = 10}+-- >>> bifoldMap1 Sum Sum (This ((1,2) :| []) (Just (Left (3 :| []))) :: This NonEmpty NonEmpty Int Int)+-- Sum {getSum = 6}+-- >>> bifoldMap1 Sum Sum (This ((1,2) :| []) (Just (Right (3 :| []))) :: This NonEmpty NonEmpty Int Int)+-- Sum {getSum = 6}+instance (Foldable1 f, Foldable1 g) => Bifoldable1 (This f g) where+ bifoldMap1 ha hb (This t r) =+ case r of+ Nothing -> foldMap1 (\(a, b) -> ha a <> hb b) t+ Just (Left ga) -> foldMap1 (\(a, b) -> ha a <> hb b) t <> foldMap1 ha ga+ Just (Right gb) -> foldMap1 (\(a, b) -> ha a <> hb b) t <> foldMap1 hb gb+ {-# INLINE bifoldMap1 #-}++-- | Foldable1 instance - folds over at least one b value+--+-- >>> import Data.Semigroup (Sum(..))+-- >>> foldMap1 Sum (This ((1,2) :| [(3,4)]) Nothing :: This NonEmpty NonEmpty Int Int)+-- Sum {getSum = 6}+-- >>> foldMap1 Sum (This ((1,2) :| []) (Just (Left (3 :| []))) :: This NonEmpty NonEmpty Int Int)+-- Sum {getSum = 2}+-- >>> foldMap1 Sum (This ((1,2) :| []) (Just (Right (3 :| []))) :: This NonEmpty NonEmpty Int Int)+-- Sum {getSum = 5}+instance (Foldable1 f, Foldable1 g) => Foldable1 (This f g a) where+ foldMap1 h (This t r) =+ case r of+ Nothing -> foldMap1 (\(_, b) -> h b) t+ Just (Left _) -> foldMap1 (\(_, b) -> h b) t+ Just (Right gb) -> foldMap1 (\(_, b) -> h b) t <> foldMap1 h gb+ {-# INLINE foldMap1 #-}++-- | Traversable instance - traverses over b values+-- Implemented using traverseB+instance (Traversable f, Traversable g) => Traversable (This f g a) where+ traverse = traverseB++-- | Traversable1 instance - traverses over at least one b value+-- Implemented using traverseB1+instance (Traversable1 f, Traversable1 g) => Traversable1 (This f g a) where+ traverse1 = traverseB1++-- | Bitraversable instance - traverses over both type parameters+-- Implemented using traverseA and traverseB+--+-- >>> bitraverse Just Just (This [(1,2)] Nothing :: This [] NonEmpty Int Int)+-- Just (This [(1,2)] Nothing)+-- >>> bitraverse (\x -> if x > 0 then Just x else Nothing) Just (This [(1,2)] Nothing :: This [] NonEmpty Int Int)+-- Just (This [(1,2)] Nothing)+-- >>> bitraverse (\x -> if x > 0 then Just x else Nothing) Just (This [(-1,2)] Nothing :: This [] NonEmpty Int Int)+-- Nothing+instance (Traversable f, Traversable g) => Bitraversable (This f g) where+ bitraverse ha hb (This t r) =+ This+ <$> traverse (\(a, b) -> (,) <$> ha a <*> hb b) t+ <*> traverse traverseEither r+ where+ traverseEither (Left ga) = Left <$> traverse ha ga+ traverseEither (Right gb) = Right <$> traverse hb gb+ {-# INLINE bitraverse #-}++-- | Bitraversable1 instance - traverses over both type parameters with at least one value+-- Implemented using traverseA and traverseB with Apply+--+-- >>> bitraverse1 (Just) (Just) (This ((1,2) :| []) Nothing :: This NonEmpty NonEmpty Int Int)+-- Just (This ((1,2) :| []) Nothing)+instance (Traversable1 f, Traversable1 g) => Bitraversable1 (This f g) where+ bitraverse1 ha hb (This t r) =+ let tResult = traverse1 (\(a, b) -> (,) <$> ha a <.> hb b) t+ in case r of+ Nothing -> (`This` Nothing) <$> tResult+ Just (Left ga) -> (\t' ga' -> This t' (Just (Left ga'))) <$> tResult <.> traverse1 ha ga+ Just (Right gb) -> (\t' gb' -> This t' (Just (Right gb'))) <$> tResult <.> traverse1 hb gb+ {-# INLINE bitraverse1 #-}++-- | Semialign type class - aligns two functors into a This value+--+-- >>> align [1,2,3] [4,5] :: This [] NonEmpty Int Int+-- This [(1,4),(2,5)] (Just (Left (3 :| [])))+-- >>> align [1,2] [3,4,5] :: This [] NonEmpty Int Int+-- This [(1,3),(2,4)] (Just (Right (5 :| [])))+-- >>> align [1,2] [3,4] :: This [] NonEmpty Int Int+-- This [(1,3),(2,4)] Nothing+--+-- = Laws+--+-- [Naturality (Bifunctoriality)]+--+-- Mapping over the aligned result is the same as mapping over the inputs first:+--+-- @bimap f g (align xs ys) ≡ align (fmap f xs) (fmap g ys)@+--+-- [Symmetry]+--+-- Aligning x with y should be the same as aligning y with x and swapping:+--+-- @align x y ≡ swap (align y x)@+--+-- [Coherence with alignWith]+--+-- The relationship between 'align' and 'alignWith' must be consistent:+--+-- @align x y ≡ alignWith id id id x y@+--+-- @alignWith f g h x y ≡+-- let This t r = align x y+-- in This (fmap f t) (fmap (bimap (fmap g) (fmap h)) r)@+--+-- [Preservation of structure]+--+-- No elements should be duplicated or lost. The total number of elements+-- in matched pairs plus elements in leftovers equals the total input elements.+--+-- See 'semialignNaturality', 'semialignSymmetry', 'semialignCoherence' for+-- testable property functions.+class (Functor f, Functor g) => Semialign f g | f -> g where+ align ::+ f a ->+ f b ->+ This f g a b+ align =+ alignWith id id id++ -- | Align with a transformation function+ --+ -- >>> alignWith (\(a,b) -> (a*10, b*100)) (*10) (*100) [1,2,3] [4,5] :: This [] NonEmpty Int Int+ -- This [(10,400),(20,500)] (Just (Left (30 :| [])))+ alignWith ::+ ((a, b) -> (c, d)) ->+ (a -> c) ->+ (b -> d) ->+ f a ->+ f b ->+ This f g c d+ alignWith f g h t1 t2 =+ case align t1 t2 of+ This t r ->+ This (fmap f t) (fmap (bimap (fmap g) (fmap h)) r)++ {-# MINIMAL align | alignWith #-}++ -- | Simplified alignWith using bimap+ --+ -- >>> alignWith' (*10) (*100) [1,2,3] [4,5] :: This [] NonEmpty Int Int+ -- This [(10,400),(20,500)] (Just (Left (30 :| [])))+ alignWith' ::+ (a -> c) ->+ (b -> d) ->+ f a ->+ f b ->+ This f g c d+ alignWith' f g =+ alignWith (bimap f g) f g+ {-# INLINE alignWith' #-}++-- * Fusion rules+--+-- These RULES enable GHC to fuse operations for better performance,+-- eliminating intermediate This allocations where possible.+--+-- Phase [2] ensures these fire after class method specialization,+-- allowing them to see concrete instances while avoiding conflicts+-- with earlier optimization phases.++{-# RULES++-- Naturality fusion: fuse bimap into align+-- Implements the semialignNaturality law as a rewrite rule+"semialign/naturality" [2] forall f g xs ys.+ bimap f g (align xs ys) = alignWith (bimap f g) f g xs ys++-- Composition fusion for alignWith followed by bimap+"alignWith/bimap" [2] forall w x y k l xs ys.+ bimap k l (alignWith w x y xs ys) =+ alignWith (bimap k l . w) (k . x) (l . y) xs ys++-- Symmetry via swap: align x y = swap (align y x)+-- Can enable other optimizations when combined with swap rules+"align/swap/symmetry" [2] forall x y.+ swap (align y x) = align x y++-- fmap can be expressed as bimap with identity on first param+-- Allows bimap rules to catch fmap patterns+"fmap/as/bimap" [2] forall f (x :: This [] NonEmpty a b).+ fmap f x = bimap id f x++ #-}++-- | Semialign instance for Identity - always produces a perfect match+--+-- >>> align (Identity 1) (Identity 2)+-- This (Identity (1,2)) Nothing+instance Semialign Identity Identity where+ align (Identity a) (Identity b) =+ This (Identity (a, b)) Nothing+ {-# INLINE align #-}++-- | Semialign instance for lists - aligns elements pairwise+--+-- >>> align [1,2,3] [4,5] :: This [] NonEmpty Int Int+-- This [(1,4),(2,5)] (Just (Left (3 :| [])))+-- >>> align [1,2] [3,4,5] :: This [] NonEmpty Int Int+-- This [(1,3),(2,4)] (Just (Right (5 :| [])))+-- >>> align [1,2] [3,4] :: This [] NonEmpty Int Int+-- This [(1,3),(2,4)] Nothing+-- >>> align ([] :: [Int]) [1,2] :: This [] NonEmpty Int Int+-- This [] (Just (Right (1 :| [2])))+instance Semialign [] NonEmpty where+ align (a : as) (b : bs) =+ let This t r = align as bs+ in This ((a, b) : t) r+ align (a : as) [] =+ This [] (Just (Left (a :| as)))+ align [] (b : bs) =+ This [] (Just (Right (b :| bs)))+ align [] [] =+ This [] Nothing+ {-# INLINABLE align #-}++-- | Semialign instance for Maybe - aligns optional values+--+-- >>> align (Just 1) (Just 2) :: This Maybe Identity Int Int+-- This (Just (1,2)) Nothing+-- >>> align (Just 1) Nothing :: This Maybe Identity Int Int+-- This Nothing (Just (Left (Identity 1)))+-- >>> align Nothing (Just 2) :: This Maybe Identity Int Int+-- This Nothing (Just (Right (Identity 2)))+-- >>> align Nothing Nothing :: This Maybe Identity Int Int+-- This Nothing Nothing+instance Semialign Maybe Identity where+ align (Just a) (Just b) =+ This (Just (a, b)) Nothing+ align (Just a) Nothing =+ This Nothing (Just (Left (Identity a)))+ align Nothing (Just b) =+ This Nothing (Just (Right (Identity b)))+ align Nothing Nothing =+ This Nothing Nothing+ {-# INLINABLE align #-}++-- | Semialign instance for NonEmpty - aligns non-empty lists+--+-- >>> align (1 :| [2,3]) (4 :| [5])+-- This ((1,4) :| [(2,5)]) (Just (Left (3 :| [])))+-- >>> align (1 :| [2]) (3 :| [4,5])+-- This ((1,3) :| [(2,4)]) (Just (Right (5 :| [])))+-- >>> align (1 :| [2]) (3 :| [4])+-- This ((1,3) :| [(2,4)]) Nothing+-- >>> align (1 :| []) (2 :| [])+-- This ((1,2) :| []) Nothing+instance Semialign NonEmpty NonEmpty where+ align (h1 :| []) (h2 :| []) =+ This ((h1, h2) :| []) Nothing+ align (h1 :| i1 : r1) (h2 :| []) =+ This ((h1, h2) :| []) (Just (Left (i1 :| r1)))+ align (h1 :| []) (h2 :| i2 : r2) =+ This ((h1, h2) :| []) (Just (Right (i2 :| r2)))+ align (h1 :| i1 : r1) (h2 :| i2 : r2) =+ let This t r = align (i1 :| r1) (i2 :| r2)+ in This ((h1, h2) `NonEmpty.cons` t) r+ {-# INLINABLE align #-}++-- | Semialign instance for ZipList - delegates to list alignment+--+-- >>> import Control.Lens (view)+-- >>> view these (align (ZipList [1,2,3]) (ZipList [4,5]))+-- ZipList {getZipList = [(1,4),(2,5)]}+instance Semialign ZipList NonEmpty where+ align (ZipList a) (ZipList b) =+ over these ZipList (align a b)+ {-# INLINABLE align #-}++-- | Semialign instance for Seq - aligns sequences element-wise+--+-- >>> align (Seq.fromList [1,2,3]) (Seq.fromList [4,5]) :: This Seq NonEmpty Int Int+-- This (fromList [(1,4),(2,5)]) (Just (Left (3 :| [])))+-- >>> align (Seq.fromList [1,2]) (Seq.fromList [3,4,5]) :: This Seq NonEmpty Int Int+-- This (fromList [(1,3),(2,4)]) (Just (Right (5 :| [])))+-- >>> align (Seq.fromList [1,2]) (Seq.fromList [3,4]) :: This Seq NonEmpty Int Int+-- This (fromList [(1,3),(2,4)]) Nothing+instance Semialign Seq NonEmpty where+ align sa sb = case (Seq.viewl sa, Seq.viewl sb) of+ (Seq.EmptyL, Seq.EmptyL) -> This Seq.empty Nothing+ (a Seq.:< as, Seq.EmptyL) -> This Seq.empty (Just (Left (a :| toList as)))+ (Seq.EmptyL, b Seq.:< bs) -> This Seq.empty (Just (Right (b :| toList bs)))+ (a Seq.:< as, b Seq.:< bs) ->+ let This t r = align as bs+ in This ((a, b) Seq.<| t) r+ where+ toList s = case Seq.viewl s of+ Seq.EmptyL -> []+ x Seq.:< xs -> x : toList xs+ {-# INLINABLE align #-}++-- | Semialign instance for Vector - aligns vectors element-wise+--+-- >>> align (Vector.fromList [1,2,3]) (Vector.fromList [4,5]) :: This Vector NonEmpty Int Int+-- This [(1,4),(2,5)] (Just (Left (3 :| [])))+-- >>> align (Vector.fromList [1,2]) (Vector.fromList [3,4,5]) :: This Vector NonEmpty Int Int+-- This [(1,3),(2,4)] (Just (Right (5 :| [])))+instance Semialign Vector NonEmpty where+ align va vb =+ let minLen = min (Vector.length va) (Vector.length vb)+ paired = Vector.zip (Vector.take minLen va) (Vector.take minLen vb)+ leftover+ | Vector.length va > minLen =+ case Vector.toList (Vector.drop minLen va) of+ [] -> Nothing+ (x : xs) -> Just (Left (x :| xs))+ | Vector.length vb > minLen =+ case Vector.toList (Vector.drop minLen vb) of+ [] -> Nothing+ (y : ys) -> Just (Right (y :| ys))+ | otherwise = Nothing+ in This paired leftover+ {-# INLINABLE align #-}++-- | Semialign instance for Map - aligns by keys+--+-- >>> let m1 = Map.fromList [(1,'a'),(2,'b'),(3,'c')]+-- >>> let m2 = Map.fromList [(2,'x'),(3,'y'),(4,'z')]+-- >>> align m1 m2 :: This (Map Int) (Map Int) Char Char+-- This (fromList [(2,('b','x')),(3,('c','y'))]) (Just (Left (fromList [(1,'a')])))+instance (Ord k) => Semialign (Map k) (Map k) where+ align m1 m2 =+ let both = Map.intersectionWith (,) m1 m2+ onlyLeft = Map.difference m1 m2+ onlyRight = Map.difference m2 m1+ leftover = case (Map.null onlyLeft, Map.null onlyRight) of+ (True, True) -> Nothing+ (False, True) -> Just (Left onlyLeft)+ (True, False) -> Just (Right onlyRight)+ (False, False) -> Just (Left onlyLeft) -- Left takes precedence+ in This both leftover+ {-# INLINABLE align #-}++-- | Semialign instance for IntMap - aligns by Int keys+--+-- >>> let m1 = IntMap.fromList [(1,'a'),(2,'b'),(3,'c')]+-- >>> let m2 = IntMap.fromList [(2,'x'),(3,'y'),(4,'z')]+-- >>> align m1 m2 :: This IntMap IntMap Char Char+-- This (fromList [(2,('b','x')),(3,('c','y'))]) (Just (Left (fromList [(1,'a')])))+instance Semialign IntMap IntMap where+ align m1 m2 =+ let both = IntMap.intersectionWith (,) m1 m2+ onlyLeft = IntMap.difference m1 m2+ onlyRight = IntMap.difference m2 m1+ leftover = case (IntMap.null onlyLeft, IntMap.null onlyRight) of+ (True, True) -> Nothing+ (False, True) -> Just (Left onlyLeft)+ (True, False) -> Just (Right onlyRight)+ (False, False) -> Just (Left onlyLeft)+ in This both leftover+ {-# INLINABLE align #-}++-- | Semialign instance for functions - pointwise alignment+--+-- >>> let f = align (+1) (*2) :: This ((->) Int) Identity Int Int+-- >>> view these f 5+-- (6,10)+instance Semialign ((->) r) Identity where+ align f g = This (\r -> (f r, g r)) Nothing+ {-# INLINE align #-}++-- | Semialign instance for pairs with Monoid first component+--+-- >>> align (mempty :: String, 1) ("", 2) :: This ((,) String) Identity Int Int+-- This ("",(1,2)) Nothing+-- >>> align ("left", 1) ("right", 2) :: This ((,) String) Identity Int Int+-- This ("leftright",(1,2)) Nothing+instance (Monoid e) => Semialign ((,) e) Identity where+ align (e1, a) (e2, b) = This (e1 <> e2, (a, b)) Nothing+ {-# INLINE align #-}++-- | Semialign instance for Const - trivial alignment+--+-- >>> align (Const "left") (Const "right") :: This (Const String) Identity Int Int+-- This (Const "left") Nothing+instance Semialign (Const m) Identity where+ align (Const a) (Const _) = This (Const a) Nothing+ {-# INLINE align #-}++-- | Align type class - Semialign with an empty structure+--+-- This is to Semialign as Applicative is to Apply.+-- The relationship: Semialign : Apply :: Align : Applicative+--+-- The 'nil' method provides an empty structure, which acts as an identity+-- for alignment operations.+--+-- = Laws+--+-- In addition to the Semialign laws, Align instances must satisfy:+--+-- [Right identity]+--+-- Aligning with nil on the right produces only left leftovers:+--+-- @align x nil ≡ This nil (toLeftover x)@+--+-- [Left identity]+--+-- Aligning with nil on the left produces only right leftovers:+--+-- @align nil y ≡ This nil (toRightover y)@+--+-- [Empty alignment]+--+-- Aligning nil with nil produces an empty result:+--+-- @align nil nil ≡ This nil Nothing@+--+-- Where 'toLeftover' and 'toRightover' convert non-empty structures to the+-- leftover type 'g'. For empty inputs, these return 'Nothing'.+--+-- Not all Semialign instances can be Align instances. For example, NonEmpty+-- cannot be Align because there is no empty NonEmpty.+--+-- >>> align (nil :: [Int]) [1,2] :: This [] NonEmpty Int Int+-- This [] (Just (Right (1 :| [2])))+-- >>> align [1,2] (nil :: [Int]) :: This [] NonEmpty Int Int+-- This [] (Just (Left (1 :| [2])))+-- >>> align (nil :: Maybe Int) (Just 5) :: This Maybe Identity Int Int+-- This Nothing (Just (Right (Identity 5)))+--+-- See 'alignRightIdentity', 'alignLeftIdentity', 'alignEmpty' for+-- testable property functions.+class (Semialign f g) => Align f g where+ -- | The empty structure - identity for alignment+ nil :: f a++-- | Align instance for lists+--+-- >>> nil :: [Int]+-- []+-- >>> align nil [1,2,3] :: This [] NonEmpty Int Int+-- This [] (Just (Right (1 :| [2,3])))+instance Align [] NonEmpty where+ nil = []+ {-# INLINE nil #-}++-- | Align instance for Maybe+--+-- >>> nil :: Maybe Int+-- Nothing+-- >>> align nil (Just 42) :: This Maybe Identity Int Int+-- This Nothing (Just (Right (Identity 42)))+instance Align Maybe Identity where+ nil = Nothing+ {-# INLINE nil #-}++-- | Align instance for ZipList+--+-- >>> nil :: ZipList Int+-- ZipList {getZipList = []}+-- >>> import Control.Lens (view)+-- >>> view these (align nil (ZipList [1,2]))+-- ZipList {getZipList = []}+instance Align ZipList NonEmpty where+ nil = ZipList []+ {-# INLINE nil #-}++-- | Align instance for Seq+--+-- >>> nil :: Seq Int+-- fromList []+-- >>> align nil (Seq.fromList [1,2]) :: This Seq NonEmpty Int Int+-- This (fromList []) (Just (Right (1 :| [2])))+instance Align Seq NonEmpty where+ nil = Seq.empty+ {-# INLINE nil #-}++-- | Align instance for Vector+--+-- >>> nil :: Vector Int+-- []+-- >>> align nil (Vector.fromList [1,2]) :: This Vector NonEmpty Int Int+-- This [] (Just (Right (1 :| [2])))+instance Align Vector NonEmpty where+ nil = Vector.empty+ {-# INLINE nil #-}++-- | Align instance for Map+--+-- >>> nil :: Map Int Char+-- fromList []+-- >>> let m = Map.fromList [(1,'a'),(2,'b')]+-- >>> align nil m :: This (Map Int) (Map Int) Char Char+-- This (fromList []) (Just (Right (fromList [(1,'a'),(2,'b')])))+instance (Ord k) => Align (Map k) (Map k) where+ nil = Map.empty+ {-# INLINE nil #-}++-- | Align instance for IntMap+--+-- >>> nil :: IntMap Char+-- fromList []+-- >>> let m = IntMap.fromList [(1,'a'),(2,'b')]+-- >>> align nil m :: This IntMap IntMap Char Char+-- This (fromList []) (Just (Right (fromList [(1,'a'),(2,'b')])))+instance Align IntMap IntMap where+ nil = IntMap.empty+ {-# INLINE nil #-}++-- | Align instance for Const with Monoid+--+-- >>> nil :: Const String Int+-- Const ""+instance (Monoid m) => Align (Const m) Identity where+ nil = Const mempty+ {-# INLINE nil #-}++-- Note: Product and Compose instances would require more complex type machinery+-- and are omitted for simplicity. They could be added with careful handling of+-- the leftover types.++-- | Unalign type class - recover original functors from alignment+--+-- Not all Semialign instances can be Unalign. This class is for functors+-- where the alignment can be reversed without loss of information.+--+-- The Unalign class provides the inverse of 'align', allowing you to recover+-- the original two functors from a 'This' value. This is only possible for+-- container-like functors that support both unzipping and merging operations.+--+-- = Laws+--+-- [Roundtrip]+--+-- The fundamental law is that unalign inverts align:+--+-- @unalign (align xs ys) ≡ (xs, ys)@+--+-- [Naturality]+--+-- Unalign commutes with fmap on both sides:+--+-- @bimap (fmap f) (fmap g) (unalign t) ≡ unalign (bimap f g t)@+--+-- [Isomorphism]+--+-- The 'aligned' Iso satisfies the isomorphism laws:+--+-- @from aligned . to aligned ≡ id@+-- @to aligned . from aligned ≡ id@+--+-- Where @to aligned = uncurry align@ and @from aligned = unalign@.+--+-- = Important Notes+--+-- Not all Semialign instances can be Unalign instances:+--+-- * Sequence types ([], Maybe, NonEmpty, Vector, Seq, etc.) ✓ Can unalign+-- * Function types ((->) r) ✗ Cannot meaningfully merge functions with constants+-- * Pair types ((,) e) ✗ Would require duplicating the first component+-- * Map and IntMap ✗ Violate roundtrip law when both sides have leftovers+--+-- For Map and IntMap, when both sides have leftovers, 'align' keeps only left+-- leftovers (left takes precedence), so 'unalign' cannot recover the original+-- right-side keys. We choose not to provide these instances to keep the type+-- class lawful.+--+-- See 'unalignRoundtrip' and 'unalignNaturality' for testable property functions.+--+-- >>> unalign (align [1,2,3] [10,20] :: This [] NonEmpty Int Int)+-- ([1,2,3],[10,20])+-- >>> unalign (align (Just 1) Nothing :: This Maybe Identity Int Int)+-- (Just 1,Nothing)+class (Semialign f g) => Unalign f g where+ {-# MINIMAL unalign #-}++ -- | Recover the original functors from an aligned result+ unalign :: This f g a b -> (f a, f b)++ -- | Unalign with transformation - transforms both sides during unalignment+ --+ -- This is the dual of 'alignWith': while @alignWith@ transforms during alignment,+ -- @unalignWith@ transforms during unalignment.+ --+ -- >>> unalignWith (*10) (*100) (This [(1,2),(3,4)] Nothing :: This [] NonEmpty Int Int)+ -- ([10,30],[200,400])+ -- >>> unalignWith (*10) (*100) (This [(1,2)] (Just (Left (3 :| []))) :: This [] NonEmpty Int Int)+ -- ([10,30],[200])+ -- >>> unalignWith (*10) (*100) (This [(1,2)] (Just (Right (5 :| []))) :: This [] NonEmpty Int Int)+ -- ([10],[200,500])+ unalignWith ::+ (a -> c) ->+ (b -> d) ->+ This f g a b ->+ (f c, f d)+ unalignWith f g = unalign . bimap f g+ {-# INLINE unalignWith #-}++ -- | Isomorphism between a pair of functors and their alignment+ --+ -- This witnesses that @(f a, f b)@ and @This f g a b@ are isomorphic,+ -- meaning alignment is completely lossless for Unalign instances.+ --+ -- >>> import Control.Lens (from, view)+ -- >>> view aligned ([1,2,3], [10,20]) :: This [] NonEmpty Int Int+ -- This [(1,10),(2,20)] (Just (Left (3 :| [])))+ -- >>> view (from aligned) (This [(1,10),(2,20)] (Just (Left (3 :| [])))) :: ([Int], [Int])+ -- ([1,2,3],[10,20])+ aligned :: Iso' (f a, f b) (This f g a b)+ aligned = iso (uncurry align) unalign+ {-# INLINE aligned #-}++-- | Isomorphism between an aligned result and a pair of functors+--+-- This is the inverse of 'aligned', providing a direct view from+-- @This f g a b@ to @(f a, f b)@.+--+-- >>> import Control.Lens (view, from)+-- >>> view unaligned (This [(1,10),(2,20)] (Just (Left (3 :| [])))) :: ([Int], [Int])+-- ([1,2,3],[10,20])+-- >>> view (from unaligned) ([1,2,3], [10,20]) :: This [] NonEmpty Int Int+-- This [(1,10),(2,20)] (Just (Left (3 :| [])))+unaligned :: (Unalign f g) => Iso' (This f g a b) (f a, f b)+unaligned = iso unalign (uncurry align)+{-# INLINE unaligned #-}++-- * Unalign fusion rules+--+-- Additional fusion rules specific to Unalign instances.+--+-- Phase [2] ensures these fire after class method specialization.++{-# RULES++-- Roundtrip elimination: unalign immediately after align+-- Implements the unalignRoundtrip law as a rewrite rule+"unalign/align/roundtrip" [2] forall xs ys.+ unalign (align xs ys) = (xs, ys)++-- Naturality for unalign: push bimap through unalign+-- Implements the unalignNaturality law as a rewrite rule+"unalign/bimap/naturality" [2] forall f g this.+ bimap (fmap f) (fmap g) (unalign this) = unalign (bimap f g this)++-- unalignWith/align roundtrip with transformation+-- Combines roundtrip elimination with transformation fusion+"unalignWith/align" [2] forall f g xs ys.+ unalignWith f g (align xs ys) = (fmap f xs, fmap g ys)++ #-}++-- | Unalign instance for Identity - simply unwrap+--+-- >>> unalign (align (Identity 1) (Identity 2) :: This Identity Identity Int Int)+-- (Identity 1,Identity 2)+instance Unalign Identity Identity where+ unalign (This (Identity (a, b)) _) = (Identity a, Identity b)+ {-# INLINE unalign #-}++-- | Unalign instance for lists - unzip and append leftovers+--+-- >>> unalign (align [1,2,3] [10,20] :: This [] NonEmpty Int Int)+-- ([1,2,3],[10,20])+-- >>> unalign (align [1,2] [10,20,30] :: This [] NonEmpty Int Int)+-- ([1,2],[10,20,30])+-- >>> unalign (align [1,2] [10,20] :: This [] NonEmpty Int Int)+-- ([1,2],[10,20])+instance Unalign [] NonEmpty where+ unalign (This pairs mleftover) =+ let (as, bs) = List.unzip pairs+ in case mleftover of+ Nothing -> (as, bs)+ Just (Left ga) -> (as ++ toList ga, bs)+ Just (Right gb) -> (as, bs ++ toList gb)+ where+ toList (x :| xs) = x : xs+ {-# INLINABLE unalign #-}++-- | Unalign instance for Maybe - reconstruct from pairs or leftovers+--+-- >>> unalign (align (Just 1) (Just 2) :: This Maybe Identity Int Int)+-- (Just 1,Just 2)+-- >>> unalign (align (Just 1) Nothing :: This Maybe Identity Int Int)+-- (Just 1,Nothing)+-- >>> unalign (align Nothing (Just 2) :: This Maybe Identity Int Int)+-- (Nothing,Just 2)+-- >>> unalign (align Nothing Nothing :: This Maybe Identity Int Int)+-- (Nothing,Nothing)+instance Unalign Maybe Identity where+ unalign (This pairs mleftover) =+ case (pairs, mleftover) of+ (Nothing, Nothing) -> (Nothing, Nothing)+ (Just (a, b), Nothing) -> (Just a, Just b)+ (Nothing, Just (Left (Identity a))) -> (Just a, Nothing)+ (Nothing, Just (Right (Identity b))) -> (Nothing, Just b)+ -- The cases with both pairs and leftovers are impossible for Maybe+ _ -> (Nothing, Nothing)+ {-# INLINE unalign #-}++-- | Unalign instance for NonEmpty - unzip and append leftovers+--+-- >>> unalign (align (1 :| [2,3]) (10 :| [20]) :: This NonEmpty NonEmpty Int Int)+-- (1 :| [2,3],10 :| [20])+-- >>> unalign (align (1 :| [2]) (10 :| [20,30]) :: This NonEmpty NonEmpty Int Int)+-- (1 :| [2],10 :| [20,30])+-- >>> unalign (align (1 :| [2]) (10 :| [20]) :: This NonEmpty NonEmpty Int Int)+-- (1 :| [2],10 :| [20])+instance Unalign NonEmpty NonEmpty where+ unalign (This pairs mleftover) =+ let (leftAs, leftBs) = unzipNonEmpty pairs+ in case mleftover of+ Nothing -> (leftAs, leftBs)+ Just (Left ga) -> (leftAs <> ga, leftBs)+ Just (Right gb) -> (leftAs, leftBs <> gb)+ where+ unzipNonEmpty ((x, y) :| rest) =+ let (xs, ys) = List.unzip rest+ in (x :| xs, y :| ys)+ {-# INLINABLE unalign #-}++-- | Unalign instance for ZipList - delegates to list unalign+--+-- >>> unalign (align (ZipList [1,2,3]) (ZipList [10,20]) :: This ZipList NonEmpty Int Int)+-- (ZipList {getZipList = [1,2,3]},ZipList {getZipList = [10,20]})+instance Unalign ZipList NonEmpty where+ unalign t =+ let (as, bs) = unalign (over these getZipList t)+ in (ZipList as, ZipList bs)+ {-# INLINE unalign #-}++-- | Unalign instance for Seq - unzip and append leftovers+--+-- >>> unalign (align (Seq.fromList [1,2,3]) (Seq.fromList [10,20]) :: This Seq NonEmpty Int Int)+-- (fromList [1,2,3],fromList [10,20])+-- >>> unalign (align (Seq.fromList [1,2]) (Seq.fromList [10,20,30]) :: This Seq NonEmpty Int Int)+-- (fromList [1,2],fromList [10,20,30])+instance Unalign Seq NonEmpty where+ unalign (This pairs mleftover) =+ let (as, bs) = unzipSeq pairs+ in case mleftover of+ Nothing -> (as, bs)+ Just (Left ga) -> (as Seq.>< fromNonEmpty ga, bs)+ Just (Right gb) -> (as, bs Seq.>< fromNonEmpty gb)+ where+ unzipSeq s = case Seq.viewl s of+ Seq.EmptyL -> (Seq.empty, Seq.empty)+ (a, b) Seq.:< rest ->+ let (as, bs) = unzipSeq rest+ in (a Seq.<| as, b Seq.<| bs)+ fromNonEmpty (x :| xs) = x Seq.<| Seq.fromList xs+ {-# INLINE unalign #-}++-- | Unalign instance for Vector - unzip and append leftovers+--+-- >>> unalign (align (Vector.fromList [1,2,3]) (Vector.fromList [10,20]) :: This Vector NonEmpty Int Int)+-- ([1,2,3],[10,20])+-- >>> unalign (align (Vector.fromList [1,2]) (Vector.fromList [10,20,30]) :: This Vector NonEmpty Int Int)+-- ([1,2],[10,20,30])+instance Unalign Vector NonEmpty where+ unalign (This pairs mleftover) =+ let (as, bs) = Vector.unzip pairs+ in case mleftover of+ Nothing -> (as, bs)+ Just (Left ga) -> (as Vector.++ fromNonEmpty ga, bs)+ Just (Right gb) -> (as, bs Vector.++ fromNonEmpty gb)+ where+ fromNonEmpty (x :| xs) = Vector.cons x (Vector.fromList xs)+ {-# INLINABLE unalign #-}++-- Note: Map and IntMap do NOT have Unalign instances because their Semialign+-- instances violate the roundtrip law. When both sides have leftovers, align+-- keeps only left leftovers (left takes precedence), so unalign cannot recover+-- the original right-side keys. We prefer to have a lawful type class rather+-- than instances with caveats.++-- * Law-checking functions+--+-- These functions can be used in property-based tests to verify that+-- instances satisfy the required laws.++-- | Test the naturality law for Semialign+--+-- Property: @bimap f g (align xs ys) ≡ align (fmap f xs) (fmap g ys)@+--+-- >>> semialignNaturality (*10) (*100) [1,2,3] [4,5] :: Bool+-- True+-- >>> semialignNaturality (*10) (*100) [1,2] [3,4,5] :: Bool+-- True+semialignNaturality ::+ (Semialign f g, Eq1 f, Eq1 g, Eq c, Eq d) =>+ (a -> c) ->+ (b -> d) ->+ f a ->+ f b ->+ Bool+semialignNaturality f g xs ys =+ liftEq2 (==) (==) (bimap f g (align xs ys)) (align (fmap f xs) (fmap g ys))++-- | Test the symmetry law for Semialign+--+-- Property: @align x y ≡ swap (align y x)@+--+-- >>> semialignSymmetry [1,2,3] [4,5] :: Bool+-- True+-- >>> semialignSymmetry [1,2] [3,4,5] :: Bool+-- True+-- >>> semialignSymmetry (Just 1) (Just 2) :: Bool+-- True+semialignSymmetry ::+ (Semialign f g, Eq1 f, Eq1 g, Eq a, Eq b) =>+ f a ->+ f b ->+ Bool+semialignSymmetry x y =+ liftEq2 (==) (==) (align x y) (swap (align y x))++-- | Test the coherence law between align and alignWith+--+-- Property: @align x y ≡ alignWith id id id x y@+--+-- >>> semialignCoherence [1,2,3] [4,5] :: Bool+-- True+-- >>> semialignCoherence (Just 1) (Just 2) :: Bool+-- True+semialignCoherence ::+ (Semialign f g, Eq1 f, Eq1 g, Eq a, Eq b) =>+ f a ->+ f b ->+ Bool+semialignCoherence x y =+ liftEq2 (==) (==) (align x y) (alignWith id id id x y)++-- | Test the alignWith transformation law+--+-- Property: @alignWith f g h x y ≡ let This t r = align x y in This (fmap f t) (fmap (bimap (fmap g) (fmap h)) r)@+--+-- >>> semialignWithLaw (\(a,b) -> (a*10, b*100)) (*10) (*100) [1,2,3] [4,5] :: Bool+-- True+semialignWithLaw ::+ (Semialign f g, Eq1 f, Eq1 g, Eq c, Eq d) =>+ ((a, b) -> (c, d)) ->+ (a -> c) ->+ (b -> d) ->+ f a ->+ f b ->+ Bool+semialignWithLaw f g h x y =+ let result1 = alignWith f g h x y+ This t r = align x y+ result2 = This (fmap f t) (fmap (bimap (fmap g) (fmap h)) r)+ in liftEq2 (==) (==) result1 result2++-- | Test the right identity law for Align+--+-- Property: When aligning with nil on the right, paired part is empty+--+-- >>> alignRightIdentity [1,2,3] ([] :: [Char])+-- True+-- >>> alignRightIdentity (Just 42) (Nothing :: Maybe Char)+-- True+-- >>> alignRightIdentity ([] :: [Int]) ([] :: [Char])+-- True+alignRightIdentity ::+ (Align f g, Eq1 f, Eq a, Eq b) =>+ f a ->+ f b ->+ Bool+alignRightIdentity x emptyY =+ case align x emptyY of+ This t Nothing -> liftEq (==) t nil+ This t (Just (Left _)) -> liftEq (==) t nil+ _ -> False++-- | Test the left identity law for Align+--+-- Property: When aligning with nil on the left, paired part is empty+--+-- >>> alignLeftIdentity ([] :: [Char]) [1,2,3]+-- True+-- >>> alignLeftIdentity (Nothing :: Maybe Char) (Just 42)+-- True+-- >>> alignLeftIdentity ([] :: [Char]) ([] :: [Int])+-- True+alignLeftIdentity ::+ (Align f g, Eq1 f, Eq a, Eq b) =>+ f a ->+ f b ->+ Bool+alignLeftIdentity emptyX y =+ case align emptyX y of+ This t Nothing -> liftEq (==) t nil+ This t (Just (Right _)) -> liftEq (==) t nil+ _ -> False++-- | Test the empty alignment law for Align+--+-- Property: @align nil nil ≡ This nil Nothing@+--+-- This function requires proxy arguments to determine the functor and element types.+-- You can pass undefined or use type applications with @TypeApplications@.+--+-- >>> alignEmpty (undefined :: [Int]) (undefined :: [Int])+-- True+-- >>> alignEmpty (undefined :: Maybe Int) (undefined :: Maybe Int)+-- True+alignEmpty ::+ forall f g a.+ (Align f g, Eq1 f, Eq1 g, Eq a) =>+ f a ->+ f a ->+ Bool+alignEmpty _ _ =+ liftEq2 (==) (==)+ (align (nil :: f a) (nil :: f a))+ (This (nil :: f (a, a)) Nothing)++-- | Test the roundtrip law for Unalign+--+-- Property: @unalign (align xs ys) ≡ (xs, ys)@+--+-- >>> unalignRoundtrip [1,2,3] [10,20]+-- True+-- >>> unalignRoundtrip [1,2] [10,20,30]+-- True+-- >>> unalignRoundtrip (Just 1) (Just 2)+-- True+-- >>> unalignRoundtrip (Nothing :: Maybe Int) (Just 2)+-- True+unalignRoundtrip ::+ (Unalign f g, Eq1 f, Eq a, Eq b) =>+ f a ->+ f b ->+ Bool+unalignRoundtrip xs ys =+ let (xs', ys') = unalign (align xs ys)+ in liftEq (==) xs xs' && liftEq (==) ys ys'++-- | Test the naturality law for Unalign+--+-- Property: @bimap (fmap f) (fmap g) (unalign t) ≡ unalign (bimap f g t)@+--+-- >>> let t = This [(1,2),(3,4)] (Just (Left (5 :| []))) :: This [] NonEmpty Int Int+-- >>> unalignNaturality (*10) (*100) t+-- True+unalignNaturality ::+ (Unalign f g, Eq1 f, Eq c, Eq d) =>+ (a -> c) ->+ (b -> d) ->+ This f g a b ->+ Bool+unalignNaturality f g t =+ let (as, bs) = unalign t+ (as', bs') = unalign (bimap f g t)+ in liftEq (==) (fmap f as) as' && liftEq (==) (fmap g bs) bs'++-- | Lens focusing on the matched pairs component+--+-- >>> import Control.Lens (view, set)+-- >>> view these (This [(1,2),(3,4)] Nothing :: This [] NonEmpty Int Int)+-- [(1,2),(3,4)]+-- >>> set these [(5,6)] (This [(1,2)] Nothing :: This [] NonEmpty Int Int)+-- This [(5,6)] Nothing+these :: Lens (This f g a b) (This f' g a b) (f (a, b)) (f' (a, b))+these f (This e o) = fmap (`This` o) (f e)+{-# INLINE these #-}++-- | Lens focusing on the leftover component+--+-- >>> import Control.Lens (view, set)+-- >>> view those (This [(1,2)] (Just (Left (3 :| []))) :: This [] NonEmpty Int Int)+-- Just (Left (3 :| []))+-- >>> set those (Nothing :: Maybe (Either (NonEmpty Int) (NonEmpty Int))) (This [(1,2)] (Just (Left (3 :| []))) :: This [] NonEmpty Int Int)+-- This [(1,2)] Nothing+those :: Lens (This f g a b) (This f g' a b) (Maybe (Either (g a) (g b))) (Maybe (Either (g' a) (g' b)))+those f (This e o) = fmap (This e) (f o)+{-# INLINE those #-}++-- | Traversal focusing on left leftovers+--+-- >>> import Control.Lens (view, toListOf)+-- >>> toListOf thoseLeft (This [(1,2)] (Just (Left (3 :| [4]))) :: This [] NonEmpty Int Int)+-- [3 :| [4]]+-- >>> toListOf thoseLeft (This [(1,2)] (Just (Right (3 :| [4]))) :: This [] NonEmpty Int Int)+-- []+thoseLeft :: Traversal' (This f g a b) (g a)+thoseLeft = those . traverse . _Left+{-# INLINE thoseLeft #-}++-- | Traversal focusing on right leftovers+--+-- >>> import Control.Lens (toListOf)+-- >>> toListOf thoseRight (This [(1,2)] (Just (Right (3 :| [4]))) :: This [] NonEmpty Int Int)+-- [3 :| [4]]+-- >>> toListOf thoseRight (This [(1,2)] (Just (Left (3 :| [4]))) :: This [] NonEmpty Int Int)+-- []+thoseRight :: Traversal' (This f g a b) (g b)+thoseRight = those . traverse . _Right+{-# INLINE thoseRight #-}++-- | Traversal focusing on all 'a' values in This+-- Touches 'a' in the tuples (a,b) and in Left (g a)+--+-- >>> import Control.Lens (over, toListOf)+-- >>> over traverseA (*10) (This [(1,2),(3,4)] Nothing :: This [] NonEmpty Int Int)+-- This [(10,2),(30,4)] Nothing+-- >>> over traverseA (*10) (This [(1,2)] (Just (Left (3 :| [4]))) :: This [] NonEmpty Int Int)+-- This [(10,2)] (Just (Left (30 :| [40])))+-- >>> toListOf traverseA (This [(1,2),(3,4)] (Just (Left (5 :| []))) :: This [] NonEmpty Int Int)+-- [1,3,5]+traverseA ::+ (Traversable f, Traversable g) =>+ Traversal (This f g a b) (This f g a' b) a a'+traverseA h (This t r) =+ This+ <$> traverse (\(a, b) -> (,b) <$> h a) t+ <*> traverse (either (fmap Left . traverse h) (pure . Right)) r+{-# INLINABLE traverseA #-}++-- | Traversal focusing on all 'b' values in This+-- Touches 'b' in the tuples (a,b) and in Right (g b)+--+-- >>> import Control.Lens (over, toListOf)+-- >>> over traverseB (*10) (This [(1,2),(3,4)] Nothing :: This [] NonEmpty Int Int)+-- This [(1,20),(3,40)] Nothing+-- >>> over traverseB (*10) (This [(1,2)] (Just (Right (3 :| [4]))) :: This [] NonEmpty Int Int)+-- This [(1,20)] (Just (Right (30 :| [40])))+-- >>> toListOf traverseB (This [(1,2),(3,4)] (Just (Right (5 :| []))) :: This [] NonEmpty Int Int)+-- [2,4,5]+traverseB ::+ (Traversable f, Traversable g) =>+ Traversal (This f g a b) (This f g a b') b b'+traverseB h (This t r) =+ This+ <$> traverse (\(a, b) -> (a,) <$> h b) t+ <*> traverse (either (pure . Left) (fmap Right . traverse h)) r+{-# INLINABLE traverseB #-}++-- | Traversal1 focusing on all 'a' values in This (at least one)+-- Uses Apply instead of Applicative+--+-- >>> import Control.Lens (over)+-- >>> over traverseA1 (*10) (This ((1,2) :| [(3,4)]) Nothing :: This NonEmpty NonEmpty Int Int)+-- This ((10,2) :| [(30,4)]) Nothing+-- >>> over traverseA1 (*10) (This ((1,2) :| []) (Just (Left (3 :| [4]))) :: This NonEmpty NonEmpty Int Int)+-- This ((10,2) :| []) (Just (Left (30 :| [40])))+traverseA1 ::+ (Traversable1 f, Traversable1 g) =>+ Traversal1 (This f g a b) (This f g a' b) a a'+traverseA1 h (This t r) =+ let tResult = traverse1 (\(a, b) -> (,b) <$> h a) t+ in case r of+ Nothing -> (`This` Nothing) <$> tResult+ Just (Left ga) -> (\t' ga' -> This t' (Just (Left ga'))) <$> tResult <.> traverse1 h ga+ Just (Right gb) -> (\t' -> This t' (Just (Right gb))) <$> tResult+{-# INLINABLE traverseA1 #-}++-- | Traversal1 focusing on all 'b' values in This (at least one)+-- Uses Apply instead of Applicative+--+-- >>> import Control.Lens (over)+-- >>> over traverseB1 (*10) (This ((1,2) :| [(3,4)]) Nothing :: This NonEmpty NonEmpty Int Int)+-- This ((1,20) :| [(3,40)]) Nothing+-- >>> over traverseB1 (*10) (This ((1,2) :| []) (Just (Right (3 :| [4]))) :: This NonEmpty NonEmpty Int Int)+-- This ((1,20) :| []) (Just (Right (30 :| [40])))+traverseB1 ::+ (Traversable1 f, Traversable1 g) =>+ Traversal1 (This f g a b) (This f g a b') b b'+traverseB1 h (This t r) =+ let tResult = traverse1 (\(a, b) -> (a,) <$> h b) t+ in case r of+ Nothing -> (`This` Nothing) <$> tResult+ Just (Left ga) -> (\t' -> This t' (Just (Left ga))) <$> tResult+ Just (Right gb) -> (\t' gb' -> This t' (Just (Right gb'))) <$> tResult <.> traverse1 h gb+{-# INLINABLE traverseB1 #-}++-- | Fold optic over all 'a' values in This+--+-- >>> import Control.Lens (toListOf)+-- >>> toListOf foldA (This [(1,2),(3,4)] Nothing :: This [] NonEmpty Int Int)+-- [1,3]+-- >>> toListOf foldA (This [(1,2)] (Just (Left (3 :| [4]))) :: This [] NonEmpty Int Int)+-- [1,3,4]+-- >>> toListOf foldA (This [(1,2)] (Just (Right (3 :| [4]))) :: This [] NonEmpty Int Int)+-- [1]+foldA :: (Foldable f, Foldable g) => Fold (This f g a b) a+foldA h x@(This t r) =+ x <$ (traverse_ (\(a, _) -> h a) t <* traverse_ (either (traverse_ h) (pure (pure ()))) r)+{-# INLINE foldA #-}++-- | Fold optic over all 'b' values in This+--+-- >>> import Control.Lens (toListOf)+-- >>> toListOf foldB (This [(1,2),(3,4)] Nothing :: This [] NonEmpty Int Int)+-- [2,4]+-- >>> toListOf foldB (This [(1,2)] (Just (Right (3 :| [4]))) :: This [] NonEmpty Int Int)+-- [2,3,4]+-- >>> toListOf foldB (This [(1,2)] (Just (Left (3 :| [4]))) :: This [] NonEmpty Int Int)+-- [2]+foldB :: (Foldable f, Foldable g) => Fold (This f g a b) b+foldB h x@(This t r) =+ x <$ (traverse_ (\(_, b) -> h b) t <* traverse_ (either (pure (pure ())) (traverse_ h)) r)+{-# INLINE foldB #-}++-- | Fold1 optic over all 'a' values in This (at least one)+--+-- >>> import Control.Lens (toListOf)+-- >>> toListOf foldA1 (This ((1,2) :| [(3,4)]) Nothing :: This NonEmpty NonEmpty Int Int)+-- [1,3]+-- >>> toListOf foldA1 (This ((1,2) :| []) (Just (Left (3 :| [4]))) :: This NonEmpty NonEmpty Int Int)+-- [1,3,4]+foldA1 :: (Foldable1 f, Foldable1 g) => Fold1 (This f g a b) a+foldA1 h x@(This t r) =+ let ese = traverse1_ (\(a, _) -> h a) t+ in x <$ case r of+ Nothing -> ese+ Just (Left ga) -> ese .> traverse1_ h ga+ Just (Right _) -> ese+{-# INLINE foldA1 #-}++-- | Fold1 optic over all 'b' values in This (at least one)+--+-- >>> import Control.Lens (toListOf)+-- >>> toListOf foldB1 (This ((1,2) :| [(3,4)]) Nothing :: This NonEmpty NonEmpty Int Int)+-- [2,4]+-- >>> toListOf foldB1 (This ((1,2) :| []) (Just (Right (3 :| [4]))) :: This NonEmpty NonEmpty Int Int)+-- [2,3,4]+foldB1 :: (Foldable1 f, Foldable1 g) => Fold1 (This f g a b) b+foldB1 h x@(This t r) =+ let ese = traverse1_ (\(_, b) -> h b) t+ in x <$ case r of+ Nothing -> ese+ Just (Left _) -> ese+ Just (Right gb) -> ese .> traverse1_ h gb+{-# INLINE foldB1 #-}++-- | Traverse with at least one element, discarding results+-- Uses Apply to combine effects+--+-- >>> traverse1_ Just (1 :| [2,3])+-- Just ()+traverse1_ :: (Foldable1 t, Apply f) => (a -> f b) -> t a -> f ()+traverse1_ f xs = case toNonEmpty xs of+ (x :| []) -> f x $> ()+ (x :| (y : ys)) -> f x .> traverse1_ f (y :| ys)+ where+ toNonEmpty = foldMap1 (:| [])+{-# INLINABLE traverse1_ #-}
+ test/Main.hs view
@@ -0,0 +1,17 @@+module Main where++import System.Exit (exitWith)+import System.Process (rawSystem)++main :: IO ()+main =+ exitWith+ =<< rawSystem+ "cabal"+ [ "repl",+ "--with-compiler=doctest",+ "--repl-options=-w",+ "--repl-options=-Wdefault",+ "--repl-options=-Wno-inline-rule-shadowing",+ "lib:alignment"+ ]