1 {-# LANGUAGE NoRebindableSyntax #-}
2 {-# OPTIONS_GHC -Wno-orphans #-}
3 module Haccounting.Chart where
5 import Control.Applicative (Applicative(..))
6 import Control.DeepSeq (NFData(..))
7 import Control.Monad (Monad(..))
9 import Data.Eq (Eq(..))
10 import Data.Foldable (Foldable(..), all, foldr)
11 import Data.Function (($), (.), const, flip)
12 import Data.Functor (Functor(..), (<$>))
13 import Data.List.NonEmpty (NonEmpty(..))
14 import Data.Map.Strict (Map)
15 import Data.Maybe (Maybe(..), isNothing)
16 import Data.Monoid (Monoid(..))
17 import Data.Ord (Ord(..))
18 import Data.Ord (Ord(..))
19 import Data.Semigroup (Semigroup(..))
20 import Data.String (String)
21 import Data.Traversable (Traversable(..))
22 import Data.Tuple (fst, snd)
23 import Prelude (error)
24 import Text.Show (Show(..))
25 import qualified Control.Monad.Trans.Writer as MT
26 import qualified Data.List as List
27 import qualified Data.List.NonEmpty as NonEmpty
28 import qualified Data.Map.Strict as Map
29 import qualified GHC.Exts as GHC
31 import Haccounting.Math
32 import Haccounting.Rebindable
35 newtype Chart k a = Chart { unChart :: Map.Map k (a, Chart k a) }
36 deriving newtype (Eq, NFData)
37 instance (Show k, Show a) => Show (Chart k a) where
38 show = List.unlines . drawMap where
39 drawNode :: (k, (a, Chart k a)) -> [String]
40 drawNode (k, (a, ts0)) =
41 List.zipWith (<>) (List.lines (show k)) (" " <> show a : List.repeat "") <>
43 drawMap = go . Map.toList . unChart where
45 go [t] = shift "` " " " (drawNode t)
46 go (t:ts) = shift "+ " "| " (drawNode t) <> go ts
47 shift ind0 ind = List.zipWith (<>) (ind0 : List.repeat ind)
48 instance Functor (Chart k) where
49 fmap f = Chart . fmap (\(a, ch) -> (f a, fmap f ch)) . unChart
50 instance Foldable (Chart k) where
51 foldMap f = foldMap (\(a, ch) -> f a <> foldMap f ch) . unChart
52 instance Traversable (Chart k) where
55 traverse (\(a, ch) -> (,) <$> f a <*> traverse f ch) .
57 instance (Semigroup a, Ord k) => Semigroup (Chart k a) where
58 x <> y = Chart $ Map.unionWith
59 (\new old -> (fst old<>fst new, snd old<>snd new))
60 (unChart x) (unChart y)
61 instance (Semigroup a, Ord k) => Monoid (Chart k a) where
62 mempty = Chart Map.empty
63 instance (Ord k, Addable a) => Addable (Chart k a) where
64 x + y = Chart $ Map.unionWith
65 (\(ym, ya) (xm, xa) -> (xm + ym, xa + ya))
66 (unChart x) (unChart y)
67 instance (Ord k, Subable a) => Subable (Chart k a) where
68 x - y = Chart $ Map.unionWith
69 (\(ym, ya) (xm, xa) -> (xm - ym, xa - ya))
70 (unChart x) (unChart y)
72 -- ** Type 'ChartPath'
73 type ChartPath = NonEmpty.NonEmpty
75 insert :: Ord k => a -> (a -> a -> a) -> ChartPath k -> a -> Chart k a -> Chart k a
76 insert init merge p a ch = go ch p
79 k:|[] -> Chart $ Map.insertWith
80 (\_new (old, c) -> (merge a old, c))
82 k:|k1:ks -> Chart $ Map.insertWith
83 (\_new (old, c) -> (old, go c (k1:|ks)))
84 k (init, go empty (k1:|ks)) m
86 -- | Return the value (if any) associated with the given 'Path'.
87 lookup :: Ord k => ChartPath k -> Chart k a -> Maybe a
88 lookup (k:|ks) (Chart m) = do
89 (a, ms) <- Map.lookup k m
92 (k':ks') -> lookup (k':|ks') ms
94 filter :: Ord k => (a -> Bool) -> Chart k a -> Chart k (Maybe a)
96 Chart . Map.mapMaybe (\(x, m) ->
98 let fm = filter f m in
99 if not fx && all isNothing fm
101 else Just (if fx then Just x else Nothing, fm)
105 empty = Chart Map.empty
107 singleton :: Ord k => a -> ChartPath k -> a -> Chart k a
108 singleton init ks a = insert init const ks a empty
110 -- | Return a 'Map' associating each 'ChartPath' in the given 'Chart',
111 -- with its value mapped by the given function.
112 flatten :: Ord k => (x -> y) -> Chart k x -> Map (ChartPath k) y
113 flatten = flattenWithPath . const
115 flattenWithPath :: Ord k => ([k] -> x -> y) -> Chart k x -> Map (ChartPath k) y
116 flattenWithPath = go [] where
119 Map.mapKeysMonotonic (NonEmpty.reverse . flip (:|) p) (
120 Map.mapWithKey (\k (a, children) -> f (List.reverse (k : p)) a) (unChart ch)
123 (\k (_a, children) -> (go (k:p) f children :))
126 mapByDepthFirst :: Ord k => (Chart k b -> a -> b) -> Chart k a -> Chart k b
129 (\(a, ch) -> let m = mapByDepthFirst f ch in (f m a, m)) .
132 foldrPath :: Ord k => (ChartPath k -> a -> acc -> acc) -> ChartPath k -> Chart k a -> acc -> acc
133 foldrPath f = go [] . NonEmpty.toList where
135 go p (k:ks) (Chart m) acc =
136 case Map.lookup k m of
137 Just (a, ch) -> f (NonEmpty.reverse (k:|p)) a $ go (k:p) ks ch acc
140 foldrWithPath :: Ord k => (ChartPath k -> a -> acc -> acc) -> acc -> Chart k a -> acc
141 foldrWithPath f = go [] where
143 Map.foldrWithKey (\k (a, ch) acc' ->
144 f (NonEmpty.reverse (k:|p)) a
149 -- | A 'Monad' to construct a 'Chart'.
150 newtype ChartM k v m a = ChartM { unChartM ::
151 MT.WriterT (v, Chart k v) m a
152 } deriving newtype (Functor, Applicative, Monad)
154 runChartM :: Monad m => ChartM k v m a -> m (Chart k v)
156 (_a, (_v, ch)) <- MT.runWriterT (unChartM chM)
159 instance (Ord k, Monoid v, Monad m) => Semigroup (ChartM k v m a) where
160 (<>) = (Control.Monad.>>)
161 instance (Ord k, Monoid v, Monad m, Monoid a) => Monoid (ChartM k v m a) where
162 mempty = return mempty
168 ) => GHC.IsList (ChartM k v m a) where
169 type Item (ChartM k v m a) = ChartM k v m a
171 fromListN _n = mconcat