1 {-# LANGUAGE NoRebindableSyntax #-}
2 {-# OPTIONS_GHC -Wno-orphans #-}
4 module Literate.Accounting.Chart where
6 import Control.Applicative (Applicative (..))
7 import Control.DeepSeq (NFData (..))
8 import Control.Monad (Monad (..))
9 import Control.Monad.Trans.Writer qualified as MT
11 import Data.Eq (Eq (..))
12 import Data.Foldable (Foldable (..), all, foldr)
13 import Data.Function (const, flip, ($), (.))
14 import Data.Functor (Functor (..), (<$>))
15 import Data.List qualified as List
16 import Data.List.NonEmpty (NonEmpty (..))
17 import Data.List.NonEmpty qualified as NonEmpty
18 import Data.Map.Strict (Map)
19 import Data.Map.Strict qualified as Map
20 import Data.Maybe (Maybe (..), isNothing)
21 import Data.Monoid (Monoid (..))
22 import Data.Ord (Ord (..))
23 import Data.Semigroup (Semigroup (..))
24 import Data.String (String)
25 import Data.Traversable (Traversable (..))
26 import Data.Tuple (fst, snd)
27 import GHC.Exts qualified as GHC
28 import Text.Show (Show (..))
29 import Prelude (error)
31 import Literate.Accounting.Math
32 import Literate.Accounting.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
40 drawNode :: (k, (a, Chart k a)) -> [String]
41 drawNode (k, (a, ts0)) =
42 List.zipWith (<>) (List.lines (show k)) (" " <> show a : List.repeat "")
44 drawMap = go . Map.toList . unChart
47 go [t] = shift "` " " " (drawNode t)
48 go (t : ts) = shift "+ " "| " (drawNode t) <> go ts
49 shift ind0 ind = List.zipWith (<>) (ind0 : List.repeat ind)
50 instance Functor (Chart k) where
51 fmap f = Chart . fmap (\(a, ch) -> (f a, fmap f ch)) . unChart
52 instance Foldable (Chart k) where
53 foldMap f = foldMap (\(a, ch) -> f a <> foldMap f ch) . unChart
54 instance Traversable (Chart k) where
57 . traverse (\(a, ch) -> (,) <$> f a <*> traverse f ch)
59 instance (Semigroup a, Ord k) => Semigroup (Chart k a) where
63 (\new old -> (fst old <> fst new, snd old <> snd new))
66 instance (Semigroup a, Ord k) => Monoid (Chart k a) where
67 mempty = Chart Map.empty
68 instance (Ord k, Addable a) => Addable (Chart k a) where
72 (\(ym, ya) (xm, xa) -> (xm + ym, xa + ya))
75 instance (Ord k, Subable a) => Subable (Chart k a) where
79 (\(ym, ya) (xm, xa) -> (xm - ym, xa - ya))
83 -- ** Type 'ChartPath'
84 type ChartPath = NonEmpty.NonEmpty
86 type Account = ChartPath
89 --newtype Account acct = Account (NonEmpty acct)
91 insert :: Ord k => a -> (a -> a -> a) -> ChartPath k -> a -> Chart k a -> Chart k a
92 insert init merge p a ch = go ch p
98 (\_new (old, c) -> (merge a old, c))
105 (\_new (old, c) -> (old, go c (k1 :| ks)))
107 (init, go empty (k1 :| ks))
110 -- | Return the value (if any) associated with the given 'Path'.
111 lookup :: Ord k => ChartPath k -> Chart k a -> Maybe a
112 lookup (k :| ks) (Chart m) = do
113 (a, ms) <- Map.lookup k m
116 (k' : ks') -> lookup (k' :| ks') ms
118 filter :: Ord k => (a -> Bool) -> Chart k a -> Chart k (Maybe a)
124 in let fm = filter f m
125 in if not fx && all isNothing fm
127 else Just (if fx then Just x else Nothing, fm)
132 empty = Chart Map.empty
134 singleton :: Ord k => a -> ChartPath k -> a -> Chart k a
135 singleton init ks a = insert init const ks a empty
137 {- | Return a 'Map' associating each 'ChartPath' in the given 'Chart',
138 with its value mapped by the given function.
140 flatten :: Ord k => (x -> y) -> Chart k x -> Map (ChartPath k) y
141 flatten = flattenWithPath . const
143 flattenWithPath :: Ord k => ([k] -> x -> y) -> Chart k x -> Map (ChartPath k) y
144 flattenWithPath = go []
149 (NonEmpty.reverse . flip (:|) p)
150 ( Map.mapWithKey (\k (a, children) -> f (List.reverse (k : p)) a) (unChart ch)
153 (\k (_a, children) -> (go (k : p) f children :))
157 mapByDepthFirst :: Ord k => (Chart k b -> a -> b) -> Chart k a -> Chart k b
161 (\(a, ch) -> let m = mapByDepthFirst f ch in (f m a, m))
164 foldrPath :: Ord k => (ChartPath k -> a -> acc -> acc) -> ChartPath k -> Chart k a -> acc -> acc
165 foldrPath f = go [] . NonEmpty.toList
168 go p (k : ks) (Chart m) acc =
169 case Map.lookup k m of
170 Just (a, ch) -> f (NonEmpty.reverse (k :| p)) a $ go (k : p) ks ch acc
173 foldrWithPath :: Ord k => (ChartPath k -> a -> acc -> acc) -> acc -> Chart k a -> acc
174 foldrWithPath f = go []
180 (NonEmpty.reverse (k :| p))
188 -- | A 'Monad' to construct a 'Chart'.
189 newtype ChartM k v m a = ChartM
191 MT.WriterT (v, Chart k v) m a
193 deriving newtype (Functor, Applicative, Monad)
195 runChartM :: Monad m => ChartM k v m a -> m (Chart k v)
197 (_a, (_v, ch)) <- MT.runWriterT (unChartM chM)
200 instance (Ord k, Monoid v, Monad m) => Semigroup (ChartM k v m a) where
201 (<>) = (Control.Monad.>>)
202 instance (Ord k, Monoid v, Monad m, Monoid a) => Monoid (ChartM k v m a) where
203 mempty = return mempty
210 GHC.IsList (ChartM k v m a)
212 type Item (ChartM k v m a) = ChartM k v m a
214 fromListN _n = mconcat