11{-# LANGUAGE CPP #-}
2+ {-# LANGUAGE DeriveFunctor #-}
23{-# LANGUAGE FlexibleInstances #-}
34{-# LANGUAGE FunctionalDependencies #-}
45{-# LANGUAGE GeneralizedNewtypeDeriving #-}
@@ -19,6 +20,7 @@ import qualified Prelude as Prelude
1920
2021import Control.Applicative (ZipList (.. ), pure , (<$>) )
2122import Data.Bifunctor (Bifunctor (.. ))
23+ import Data.Biapplicative (Biapplicative (.. ), traverseBia )
2224import Data.Functor.Compose (Compose (.. ))
2325import Data.Functor.Identity (Identity (.. ))
2426import Data.Functor.Product (Product (.. ))
@@ -577,7 +579,47 @@ instance (Ord k) => Align (Map k) where
577579instance Ord k => Unalign (Map k ) where
578580 unalign xs = (Map. mapMaybe justHere xs, Map. mapMaybe justThere xs)
579581
580- instance Ord k => Unzip (Map k ) where unzip = unzipDefault
582+ -- A copy of (,) with a stricter bimap.
583+ newtype SBPair a b = SBPair { unSBPair :: (a , b ) }
584+
585+ instance Bifunctor SBPair where
586+ bimap f g (SBPair (a, b)) = SBPair (f a, g b)
587+
588+ instance Biapplicative SBPair where
589+ bipure a b = SBPair (a, b)
590+ biliftA2 f g (SBPair (a, b)) (SBPair (c, d)) =
591+ SBPair (f a c, g b d)
592+
593+ instance Ord k => Unzip (Map k ) where
594+ -- Map has a strict spine, so we have to build a whole one at
595+ -- once. The default instance would first build an entire
596+ -- Map filled with thunks, each of which will produce a pair,
597+ -- and then build two maps, each filled with thunks to extract
598+ -- a value from the pair. We instead build both maps at once,
599+ -- each of which will be filled with selector thunks, along
600+ -- with thunks (not in any Map) holding the applications of
601+ -- `f`.
602+ unzipWith f xs = (l, r)
603+ where
604+ ~ (l, r) = unSBPair . traverseBia (SBPair . blah) $ xs
605+ blah c = let
606+ {-# NOINLINE fc #-} -- make sure the result of f c is shared,
607+ -- and that nothing weird happens to
608+ -- keep us from getting selector thunks.
609+ {-# NOINLINE a #-} -- make sure we get selector thunks
610+ {-# NOINLINE b #-}
611+ fc = f c
612+ ~ (a, b) = fc
613+ in (a, b)
614+
615+ unzip xs = (l, r)
616+ where
617+ ~ (l, r) = unSBPair . traverseBia (SBPair . blah) $ xs
618+ blah ab = let
619+ {-# NOINLINE a #-} -- make sure we get selector thunks
620+ {-# NOINLINE b #-}
621+ ~ (a, b) = ab
622+ in (a, b)
581623
582624instance Ord k => Zip (Map k ) where
583625 zipWith = Map. intersectionWith
@@ -601,7 +643,28 @@ instance Align IntMap where
601643instance Unalign IntMap where
602644 unalign xs = (IntMap. mapMaybe justHere xs, IntMap. mapMaybe justThere xs)
603645
604- instance Unzip IntMap where unzip = unzipDefault
646+ instance Unzip IntMap where
647+ -- See notes at the Map instance
648+ unzipWith f xs = (l, r)
649+ where
650+ ~ (l, r) = unSBPair . traverseBia (SBPair . blah) $ xs
651+ blah c = let
652+ {-# NOINLINE fc #-} -- make sure the result of f c is shared,
653+ -- and that nothing weird happens to
654+ -- keep us from getting selector thunks.
655+ {-# NOINLINE a #-} -- make sure we get selector thunks
656+ {-# NOINLINE b #-}
657+ fc = f c
658+ ~ (a, b) = fc
659+ in (a, b)
660+ unzip xs = (l, r)
661+ where
662+ ~ (l, r) = unSBPair . traverseBia (SBPair . blah) $ xs
663+ blah ab = let
664+ {-# NOINLINE a #-} -- make sure we get selector thunks
665+ {-# NOINLINE b #-}
666+ ~ (a, b) = ab
667+ in (a, b)
605668
606669instance Zip IntMap where
607670 zipWith = IntMap. intersectionWith
0 commit comments