packages feed

geomancy-0.2.4.2: src/Geomancy/Tree.hs

module Geomancy.Tree
  ( Tree(..)

  , apply
  , applyWith
  , mapAccum

  , node_
  , leaf_
  , collect_

  , annotateMap
  , annotateWith
  ) where

import Data.Tree (Tree(..))
import Data.Foldable (toList)

-- * Merging annotations

{- |
  Distribute annotations down the tree without changing the type.
-}
{-# INLINEABLE apply #-}
apply :: Semigroup ann => Tree (ann, a) -> Tree (ann, a)
apply (Node (rootAnn, root) rootChildren) =
  Node
    (rootAnn, root)
    (map (applyWith (<>) rootAnn) rootChildren)

{- |
  Distribute accumulator down the tree using the accumulator function.
-}
{-# INLINEABLE applyWith #-}
applyWith
  :: (ann -> acc -> acc)
  -> acc
  -> Tree (ann, a)
  -> Tree (acc, a)
applyWith f = mapAccum next
  where
    next acc (ann, item) =
      let
        acc' = f ann acc
      in
        (acc', (acc', item))

{- |
  Transform a tree by combining branch-independent accumulator with node contents.
-}
{-# INLINEABLE mapAccum #-}
mapAccum
  :: (t -> a -> (t, b))
  -> t
  -> Tree a
  -> Tree b
mapAccum f acc (Node item children) =
  Node
    nextNode
    (map (mapAccum f nextAcc) children)
  where
    (nextAcc, nextNode) = f acc item

-- ** Shortcuts for monoidal annotation and Maybe-wrapped items

{-# INLINEABLE node_ #-}
node_ :: ann -> [Tree (ann, Maybe a)] -> Tree (ann, Maybe a)
node_ ann = Node (ann, Nothing)

{-# INLINEABLE leaf_ #-}
leaf_ :: Monoid ann => a -> Tree (ann, Maybe a)
leaf_ x = Node (mempty, Just x) []

collect_ :: Monoid ann => Tree (ann, Maybe a) -> [(ann, a)]
collect_ root = do
  (ann, Just item) <- toList $ apply root
  pure (ann, item)

-- * Adding annotations

{- |
  Annotate nodes with bottom-up monoidal summary.
-}
annotateMap
  :: Monoid ann
  => (a -> ann)
  -> Tree a
  -> Tree (ann, a)
annotateMap f =
  annotateWith f (\x anns -> f x <> mconcat anns)

{- |
  Annotate the nodes with bottom-up summary.
-}
annotateWith
  :: (a -> ann)
  -> (a -> [ann] -> ann)
  -> Tree a
  -> Tree (ann, a)
annotateWith leaf node = go
  where
    go (Node x ts) =
      case ts of
        [] ->
          Node (leaf x, x) []
        _ ->
          let
            inner = map go ts
          in
            Node
              ( node
                  x
                  (map (fst . rootLabel) inner)
              , x
              )
              inner