@@ -42,9 +42,9 @@ module Streamly.Internal.Data.Stream.IsStream.Top
4242 , leftJoin
4343 , mergeLeftJoin
4444 , hashLeftJoin
45− , outerJoin
45+ , joinOuter
4646 , mergeOuterJoin
47− , hashOuterJoin
47+ , joinOuterMap
4848 )
4949where
5050
@@ -385,7 +385,7 @@ leftJoin eq s1 s2 = Stream.evalStateT (return False) $ do
385385 else Stream.nil
386386 Nothing -> return (a, Nothing)
387387
388−-- | Like 'outerJoin' but uses a hashmap for efficiency.
388+-- | Like 'joinOuter' but uses a hashmap for efficiency.
389389--
390390-- Space: O(n)
391391--
@@ -423,13 +423,13 @@ mergeLeftJoin _eq _s1 _s2 = undefined
423423-- Time: O(m x n)
424424--
425425-- /Unimplemented/
426−{-# INLINE outerJoin #-}
427−outerJoin :: MonadIO m =>
426+{-# INLINE joinOuter #-}
427+joinOuter :: MonadIO m =>
428428 (a -> b -> Bool)
429429 -> SerialT m a
430430 -> SerialT m b
431431 -> SerialT m (Maybe a, Maybe b)
432−outerJoin eq s1 s =
432+joinOuter eq s1 s =
433433 Stream.concatM $ do
434434 arr <- Array.fromStream $ fmap (,False) s
435435 return $ go arr <> leftOver arr
@@ -470,7 +470,7 @@ outerJoin eq s1 s =
470470-- a flag. At the end go through @t m b@ and find those that are not in that
471471-- hash to return (Nothing, b).
472472--
473−-- | Like 'outerJoin' but uses a hashmap for efficiency.
473+-- | Like 'joinOuter' but uses a hashmap for efficiency.
474474--
475475-- For space efficiency use the smaller stream as the second stream.
476476--
@@ -478,13 +478,38 @@ outerJoin eq s1 s =
478478--
479479-- Time: O(m + n)
480480--
481−-- /Unimplemented/
482−{-# INLINE hashOuterJoin #-}
483−hashOuterJoin :: -- (Monad m, Hashable b) =>
484− (a -> b -> Ordering) -> t m a -> t m b -> t m (Maybe a, Maybe b)
485−hashOuterJoin _eq _s1 _s2 = undefined
481+-- /Pre-release/
482+{-# INLINE joinOuterMap #-}
483+joinOuterMap ::
484+ (IsStream t, Ord k, MonadIO m) =>
485+ t m (k, a) -> t m (k, b) -> t m (k, Maybe a, Maybe b)
486+joinOuterMap s1 s2 =
487+ Stream.concatM $ do
488+ km1 <- kvFold $ IsStream.adapt s1
489+ km2 <- kvFold $ IsStream.adapt s2
490+
491+ let res1 = Stream.map (joinAB km2) s1
492+ where
493+ joinAB km (k, a) =
494+ case k `Map.lookup` km of
495+ Just b -> (k, Just a, Just b)
496+ Nothing -> (k, Just a, Nothing)
497+
498+ let res2 = Stream.mapMaybe (joinAB km1) s2
499+ where
500+ joinAB km (k, b) =
501+ case k `Map.lookup` km of
502+ Just _ -> Nothing
503+ Nothing -> Just (k, Nothing, Just b)
504+
505+ return $ Stream.serial res1 res2
506+
507+ where
508+
509+ -- XXX Generate error if a duplicate insertion is attempted?
510+ kvFold = Stream.foldl' (\kv (k, b) -> Map.insert k b kv) Map.empty
486511
487−-- | Like 'outerJoin' but works only on sorted streams.
512+-- | Like 'joinOuter' but works only on sorted streams.
488513--
489514-- Space: O(1)
490515--
@@ -607,7 +632,7 @@ mergeDifferenceBy _eq _s1 _s2 = undefined
607632-- unionBy eq s1 s2 = s1 \`serial` (s2 `differenceBy eq` s1)
608633-- @
609634--
610−-- Similar to 'outerJoin' but not the same.
635+-- Similar to 'joinOuter' but not the same.
611636--
612637-- Space: O(n)
613638--