haskell / containers

Assorted concrete container types
https://hackage.haskell.org/package/containers
314 stars 177 forks source link

Data.Map.Internal does not export insertMin #988

Open mniip opened 5 months ago

mniip commented 5 months ago

I found myself reaching for the internals when implementing a Crosswalk instance for Map:

{- cabal:
build-depends: base, containers, these, semialign
-}
{-# LANGUAGE GHC2021, LambdaCase #-}
import Data.Map (Map)
import Data.Map.Internal qualified as Map
import Data.These
import Data.Semialign
import Data.Crosswalk

instance Crosswalk (Map k) where
  crosswalk :: Align f => (a -> f b) -> Map k a -> f (Map k b)
  crosswalk _ Map.Tip = nil
  crosswalk f (Map.Bin _ k v l r)
    = assemble k <$> (crosswalk f l `align` f v `align` crosswalk f r)

assemble :: k -> These (These (Map k a) a) (Map k a) -> Map k a
assemble k = \case
  This (This l) -> l
  This (That v) -> Map.singleton k v
  That r -> r

  These (That v) r -> Map.insertMin k v r -- ?
  This (These l v) -> Map.insertMax k v l
  These (This l) r -> Map.link2 l r

  These (These l v) r -> Map.link k v l r

The function assemble represents a general merging operation handling cases where left subtree, right subtree, and the bin's value may or may not be missing (although not all 3 missing simultaneously: empty maps follow an altogether different code path).

Data.Map.Internal has all the necessary functions for a straightforward and exact implementation of these merges (not doing extra work when a given part is missing), but for some reason insertMin is not exported.

Of course, there are plenty of ways to write this function differently, including: