module Data.Align (
Align(..)
, malign, padZip, padZipWith
, lpadZip, lpadZipWith
, rpadZip, rpadZipWith
, alignVectorWith
, Unalign(..)
, Crosswalk(..)
, Bicrosswalk(..)
) where
import Control.Applicative (ZipList(..), pure, (<$>))
import Data.Bifoldable (Bifoldable(..))
import Data.Bifunctor (Bifunctor(..))
import Data.Foldable (Foldable)
import Data.Functor.Identity
import Data.Functor.Product
import Data.IntMap (IntMap)
import Data.Map (Map)
import Data.Maybe (catMaybes)
import Data.Monoid (Monoid(..))
import Data.Sequence (Seq)
import Data.These
import Data.Vector.Generic (Vector, unstream, stream)
import Data.Vector.Fusion.Stream.Monadic (Stream(..), Step(..))
import qualified Data.IntMap as IntMap
import qualified Data.Map as Map
import qualified Data.Sequence as Seq
import qualified Data.Vector.Fusion.Stream.Monadic as Stream
import qualified Data.Vector.Fusion.Stream.Size as Stream
import Prelude
oops :: String -> a
oops = error . ("Data.Align: internal error: " ++)
class (Functor f) => Align f where
nil :: f a
align :: f a -> f b -> f (These a b)
align = alignWith id
alignWith :: (These a b -> c) -> f a -> f b -> f c
alignWith f a b = f <$> align a b
instance Align Maybe where
nil = Nothing
align Nothing Nothing = Nothing
align (Just a) Nothing = Just (This a)
align Nothing (Just b) = Just (That b)
align (Just a) (Just b) = Just (These a b)
instance Align [] where
nil = []
align xs [] = This <$> xs
align [] ys = That <$> ys
align (x:xs) (y:ys) = These x y : align xs ys
instance Align ZipList where
nil = ZipList []
align (ZipList xs) (ZipList ys) = ZipList (align xs ys)
instance Align Seq where
nil = Seq.empty
align xs ys =
case Seq.viewl xs of
Seq.EmptyL -> That <$> ys
x Seq.:< xs' ->
case Seq.viewl ys of
Seq.EmptyL -> This <$> xs
y Seq.:< ys' -> These x y Seq.<| align xs' ys'
instance (Ord k) => Align (Map k) where
nil = Map.empty
align m n = Map.unionWith merge (Map.map This m) (Map.map That n)
where merge (This a) (That b) = These a b
merge _ _ = oops "Align Map: merge"
instance Align IntMap where
nil = IntMap.empty
align m n = IntMap.unionWith merge (IntMap.map This m) (IntMap.map That n)
where merge (This a) (That b) = These a b
merge _ _ = oops "Align IntMap: merge"
instance (Align f, Align g) => Align (Product f g) where
nil = Pair nil nil
align (Pair a b) (Pair c d) = Pair (align a c) (align b d)
instance Monad m => Align (Stream m) where
nil = Stream.empty
alignWith f (Stream stepa sa na) (Stream stepb sb nb)
= Stream step (sa, sb, Nothing, False) (Stream.larger na nb)
where
step (sa, sb, Nothing, False) = do
r <- stepa sa
return $ case r of
Yield x sa' -> Skip (sa', sb, Just x, False)
Skip sa' -> Skip (sa', sb, Nothing, False)
Done -> Skip (sa, sb, Nothing, True)
step (sa, sb, av, adone) = do
r <- stepb sb
return $ case r of
Yield y sb' -> Yield (f $ maybe (That y) (`These` y) av)
(sa, sb', Nothing, adone)
Skip sb' -> Skip (sa, sb', av, adone)
Done -> case (av, adone) of
(Just x, False) -> Yield (f $ This x) (sa, sb, Nothing, adone)
(_, True) -> Done
_ -> Skip (sa, sb, Nothing, False)
alignVectorWith :: (Vector v a, Vector v b, Vector v c)
=> (These a b -> c) -> v a -> v b -> v c
alignVectorWith f x y = unstream $ alignWith f (stream x) (stream y)
malign :: (Align f, Monoid a) => f a -> f a -> f a
malign = alignWith (mergeThese mappend)
padZip :: (Align f) => f a -> f b -> f (Maybe a, Maybe b)
padZip = alignWith (fromThese Nothing Nothing . bimap Just Just)
padZipWith :: (Align f) => (Maybe a -> Maybe b -> c) -> f a -> f b -> f c
padZipWith f xs ys = uncurry f <$> padZip xs ys
lpadZipWith :: (Maybe a -> b -> c) -> [a] -> [b] -> [c]
lpadZipWith f xs ys = catMaybes $ padZipWith (\x y -> f x <$> y) xs ys
lpadZip :: [a] -> [b] -> [(Maybe a, b)]
lpadZip = lpadZipWith (,)
rpadZipWith :: (a -> Maybe b -> c) -> [a] -> [b] -> [c]
rpadZipWith f xs ys = lpadZipWith (flip f) ys xs
rpadZip :: [a] -> [b] -> [(a, Maybe b)]
rpadZip = rpadZipWith (,)
class (Align f) => Unalign f where
unalign :: f (These a b) -> (f (Maybe a), f (Maybe b))
unalign x = (fmap left x, fmap right x)
where left = these Just (const Nothing) (\a _ -> Just a)
right = these (const Nothing) Just (\_ b -> Just b)
instance Unalign Maybe
instance Unalign [] where
unalign = foldr (these a b ab) ([],[])
where a l ~(ls,rs) = (Just l :ls, Nothing:rs)
b r ~(ls,rs) = (Nothing:ls, Just r :rs)
ab l r ~(ls,rs) = (Just l :ls, Just r :rs)
instance Unalign ZipList where
unalign (ZipList xs) = (ZipList ys, ZipList zs)
where (ys, zs) = unalign xs
instance (Unalign f, Unalign g) => Unalign (Product f g) where
unalign (Pair a b) = (Pair al bl, Pair ar br)
where (al, ar) = unalign a
(bl, br) = unalign b
instance Monad m => Unalign (Stream m)
class (Functor t, Foldable t) => Crosswalk t where
crosswalk :: (Align f) => (a -> f b) -> t a -> f (t b)
crosswalk f = sequenceL . fmap f
sequenceL :: (Align f) => t (f a) -> f (t a)
sequenceL = crosswalk id
instance Crosswalk Identity where
crosswalk f (Identity a) = fmap Identity (f a)
instance Crosswalk Maybe where
crosswalk _ Nothing = nil
crosswalk f (Just a) = Just <$> f a
instance Crosswalk [] where
crosswalk _ [] = nil
crosswalk f (x:xs) = alignWith cons (f x) (crosswalk f xs)
where cons = these pure id (:)
instance Crosswalk (These a) where
crosswalk _ (This _) = nil
crosswalk f (That x) = That <$> f x
crosswalk f (These a x) = These a <$> f x
class (Bifunctor t, Bifoldable t) => Bicrosswalk t where
bicrosswalk :: (Align f) => (a -> f c) -> (b -> f d) -> t a b -> f (t c d)
bicrosswalk f g = bisequenceL . bimap f g
bisequenceL :: (Align f) => t (f a) (f b) -> f (t a b)
bisequenceL = bicrosswalk id id
instance Bicrosswalk Either where
bicrosswalk f _ (Left x) = Left <$> f x
bicrosswalk _ g (Right x) = Right <$> g x
instance Bicrosswalk These where
bicrosswalk f _ (This x) = This <$> f x
bicrosswalk _ g (That x) = That <$> g x
bicrosswalk f g (These x y) = align (f x) (g y)