unzipWith f xs == unzip (fmap f xs)Efficiency note: unzipWith produces its two results in lockstep. If you calculate unzipWith f xs and fully force either of the results, then the entire structure of the other one will be built as well. This behavior allows the garbage collector to collect each calculated pair component as soon as it dies, without having to wait for its mate to die. If you do not need this behavior, you may be better off simply calculating the sequence of pairs and using fmap to extract each component sequence.
>>> unzipWith f = Fold.unzipWithM (return . f) >>> unzipWith f fld1 fld2 = Fold.lmap f (Fold.unzip fld1 fld2)This fold terminates when both the input folds terminate. Pre-release
>>> unzipWith f = Scanl.unzipWithM (return . f) >>> unzipWith f fld1 fld2 = Scanl.lmap f (Scanl.unzip fld1 fld2)This scan terminates as soon as any of the input scans terminate. Pre-release
unzipWith f xs == unzip (fmap f xs)Efficiency note: unzipWith produces its two results in lockstep. If you calculate unzipWith f xs and fully force either of the results, then the entire structure of the other one will be built as well. This behavior allows the garbage collector to collect each calculated pair component as soon as it dies, without having to wait for its mate to die. If you do not need this behavior, you may be better off simply calculating the sequence of pairs and using fmap to extract each component sequence.
unzipWith f = unzip . map f
>>> unzipWithM k f1 f2 = Fold.lmapM k (Fold.unzip f1 f2)Pre-release
>>> unzipWithM k f1 f2 = Scanl.lmapM k (Scanl.unzip f1 f2)Pre-release
data Blerg = Blerg Int64 Bool Text Char instance EncodeRow Blerg where unzipWithEncoder k = k cons nil enc 4 where cons (Blerg a b c d) ~(as, bs, cs, ds) = (a : as, b : bs, c : cs, d : ds) nil = ([], [], [], []) enc = (((x, _, _, _) -> x) >$< param encodeField) <> (((_, x, _, _) -> x) >$< param encodeField) <> (((_, _, x, _) -> x) >$< param encodeField) <> (((_, _, _, x) -> x) >$< param encodeField)We chose ([Int64], [Bool], [Text], [Char]) as our existential type. If we instead use the default instance based on GEncodeRow then we would produce the same code as the instance below:
instance EncodeRow Blerg where unzipWithEncoder k = k cons nil enc 4 where cons (Blerg a b c d) ~(~(as, bs), ~(cs, ds)) = ((a : as, b : bs), (c : cs, d : ds)) nil = (([], []), ([], [])) enc = ((((x, _), _) -> x) >$< param encodeField) <> ((((_, x), _) -> x) >$< param encodeField) <> (((_ , (x, _)) -> x) >$< param encodeField) <> (((_ , (_, x)) -> x) >$< param encodeField)The notable difference being we don't produce a flat tuple, but instead produce a balanced tree of tuples isomorphic to the balanced tree of :*: from the generic Rep of Blerg.
>>> parUnzipWithM cfg f c1 c2 = Fold.unzipWithM f (Fold.parBuffered cfg c1) (Fold.parBuffered cfg c2)Example:
>>> delay x = threadDelay 1000000 >> print x >> return x >>> c1 = Fold.lmapM delay Fold.sum >>> c2 = Fold.lmapM delay Fold.sum >>> dst = Fold.parUnzipWithM id (pure . id) c1 c2 >>> Stream.fold dst $ (fmap (\x -> (x, x* x))) src ...