1- {-# LANGUAGE DerivingStrategies #-}
21{-# LANGUAGE FunctionalDependencies #-}
32module Money.Theory.SemanticMoney
43 ( -- * Semantic Money Classes & Primitives
54 MonetaryUnit (settle , settledAt , flowRate , rtb )
65 , IndexedValue (shift1 , flow1 )
7- , prim2 , shift2 , flow2 , shiftFlow2a , shiftFlow2b
8- , MonetaryParticle (align2 )
6+ , shift2b , flow2a , flow2b , align2a , align2b
97 -- * Semantic Money Instances
108 , BasicParticle (.. )
11- , UniversalIndex (.. )
129 , PDP_Index (.. ), PDP_Member (.. ), PDP_MemberMU , pdp_UpdateMember2
1310 -- * Re-export Monetary Types
1411 , module Money.Theory.MonetaryTypes
1512 ) where
16- --
13+ -- base
14+ import Control.Exception (assert )
15+ import Data.Tuple (swap )
16+ -- default
1717import Data.Default (Default (.. ))
1818--
1919import Money.Theory.MonetaryTypes
@@ -24,74 +24,59 @@ import Money.Theory.MonetaryTypes
2424------------------------------------------------------------------------------------------------------------------------
2525
2626-- | A monetary unit and its operators.
27- class ( MonetaryTypes mt , t ~ MT_TIME mt , v ~ MT_VALUE mt ) =>
27+ class MonetaryTypes'tv mt t v =>
2828 MonetaryUnit mt t v mu | mu -> mt where
2929 settle :: t -> mu -> mu
3030 settledAt :: mu -> t
3131 flowRate :: mu -> v
3232 rtb :: mu -> t -> v
3333
3434-- | An indexed monetary value and its 1-primitive operators.
35- class (MonetaryUnit mt t v ix , u ~ MT_UNIT mt , Monoid ix ) =>
36- IndexedValue mt t v u ix | ix -> mt where
37- shift1 :: v -> ix -> (ix , v )
38- flow1 :: v -> ix -> (ix , v )
35+ class (MonetaryUnit mt t v iv , u ~ MT_UNIT mt , Monoid iv , Eq iv ) =>
36+ IndexedValue mt t v u iv | iv -> mt where
37+ shift1 :: v -> iv -> (iv , v )
38+ flow1 :: v -> iv -> (iv , v )
3939
4040--
4141-- polymorphic 2-primitives for indexed values
4242--
4343
44- -- | 2-primitive higher order function
45- prim2 ::
46- (IndexedValue mt t v u a , IndexedValue mt t v u b ) =>
47- ((a , b ) -> (a , b )) -> t -> (a , b ) -> (a , b )
48- prim2 op t' (a, b) = op (settle t' a, settle t' b)
49-
50- -- | shift2, right side biased error term adjustment
51- shift2 ::
44+ -- | Shift value for the left side (a) or right side (b).
45+ shift2a , shift2b ::
5246 (IndexedValue mt t v u a , IndexedValue mt t v u b ) =>
5347 v -> t -> (a , b ) -> (a , b )
54- shift2 amount = prim2 op
55- where op (a, b) = let (b', amount') = shift1 amount b
56- (a', _) = shift1 (- amount') a
57- in (a', b')
58-
59- -- | flow2, right side biased error term adjustment
60- flow2 ::
48+ shift2a v t (a, b) =
49+ let (a', v') = shift1 v (settle t a)
50+ -- we assume second flow1 produces no more error term.
51+ (b', v'') = shift1 (- v') (settle t b)
52+ in assert (v'' == - v') (a', b')
53+ shift2b v t (a, b) = swap (shift2a (- v) t (b, a))
54+
55+ -- | Shifting flow for the left side (a) or right side (b).
56+ flow2a , flow2b ::
6157 (IndexedValue mt t v u a , IndexedValue mt t v u b ) =>
6258 v -> t -> (a , b ) -> (a , b )
63- flow2 r = prim2 op
64- where op (a, b) = let (b', r') = flow1 r b
65- (a', _) = flow1 (- r') a
66- in (a', b')
67-
68- -- | shiftFlow2 for the left side (a), right side biased error term adjustment
69- shiftFlow2a ::
70- (IndexedValue mt t v u a , IndexedValue mt t v u b ) =>
71- v -> t -> (a , b ) -> (a , b )
72- shiftFlow2a dr t (a, b) =
73- let ( _, b1) = flow2 (flowRate a) t (a, mempty )
74- (a', b2) = flow2 (- flowRate a + dr) t (a, mempty )
75- in (a', b <> b1 <> b2)
76-
77- -- | shiftFlow2 for the right side (b), right side biased error term adjustment
78- shiftFlow2b ::
59+ flow2a dfr t (a, b) =
60+ let (b1, fr_a) = flow1 (flowRate a) (settle t mempty )
61+ (b2, fr_a') = flow1 (- fr_a + dfr) (settle t mempty )
62+ (a', fr_a'') = flow1 (- fr_a') (settle t a)
63+ in assert (fr_a' == - fr_a'') (a', b <> b1 <> b2)
64+ flow2b dfr t (a, b) = swap (flow2a (- dfr) t (b, a))
65+
66+ -- | Value alignment 2-primitive, left-biased (a) or right-biased (b).
67+ --
68+ -- Note on side-biased operations:
69+ -- 1) Left side produces error term with which right side is adjusted accordingly, and vice versa.
70+ -- 2) The adjustment must not produce new error term, or otherwise it would require recursive adjustments.
71+ align2a , align2b ::
7972 (IndexedValue mt t v u a , IndexedValue mt t v u b ) =>
80- v -> t -> (a , b ) -> (a , b )
81- shiftFlow2b dr t (a, b) =
82- let (a1, _) = flow2 (- flowRate b) t (mempty , b)
83- (a2, b') = flow2 (flowRate b + dr) t (mempty , b)
84- in (a <> a1 <> a2, b')
85-
86- class IndexedValue mt t v u mp =>
87- MonetaryParticle mt t v u mp where
88- -- | Value alignment 2-primitive, right side biased
89- --
90- -- NOTE:
91- -- * On right side biased operations:
92- -- 1) Right side produces error term with which left side is adjusted accordingly.
93- -- 2) The adjustment must not produce new error term, or otherwise it would require recursive adjustments.
94- align2 :: forall a . IndexedValue mt t v u a => u -> u -> (mp , a ) -> (mp , a )
73+ MT_UNIT mt -> MT_UNIT mt -> (a , b ) -> (a , b )
74+ align2a u u' (a, b) = (a', b')
75+ where fr = flowRate a
76+ (fr', e) = if u' == 0 then (0 , fr `mt_v_mul_u` u) else fr `mt_v_mul_u_qr_u` (u, u')
77+ a' = fst (flow1 fr' a)
78+ b' = fst (flow1 (e + flowRate b) b)
79+ align2b u u' (a, b) = swap (align2a u u' (b, a))
9580
9681------------------------------------------------------------------------------------------------------------------------
9782-- Basic Particle: building block for indexes
@@ -103,7 +88,7 @@ data BasicParticle mt = BasicParticle
10388 , bp_flow_rate :: MT_VALUE mt
10489 }
10590
106- deriving stock instance MonetaryTypes mt => Eq (BasicParticle mt )
91+ deriving instance MonetaryTypes mt => Eq (BasicParticle mt )
10792
10893instance MonetaryTypes mt => Semigroup (BasicParticle mt ) where
10994 a@ (BasicParticle t1 _ _) <> b@ (BasicParticle t2 _ _) = BasicParticle t' (sv1 + sv2) (r1 + r2)
@@ -116,7 +101,7 @@ instance MonetaryTypes mt => Semigroup (BasicParticle mt) where
116101instance MonetaryTypes mt => Monoid (BasicParticle mt ) where
117102 mempty = BasicParticle 0 0 0
118103
119- instance ( MonetaryTypes mt , t ~ MT_TIME mt , v ~ MT_VALUE mt ) =>
104+ instance MonetaryTypes'tv mt t v =>
120105 MonetaryUnit mt t v (BasicParticle mt ) where
121106 settle t' a = a { bp_settled_at = t'
122107 , bp_settled_value = rtb a t'
@@ -125,34 +110,11 @@ instance (MonetaryTypes mt, t ~ MT_TIME mt, v ~ MT_VALUE mt) =>
125110 flowRate = bp_flow_rate
126111 rtb (BasicParticle t s r) t' = r `mt_v_mul_t` (t' - t) + s
127112
128- instance ( MonetaryTypes mt , t ~ MT_TIME mt , v ~ MT_VALUE mt , u ~ MT_UNIT mt ) =>
113+ instance MonetaryTypes'tvu mt t v u =>
129114 IndexedValue mt t v u (BasicParticle mt ) where
130-
131115 shift1 x a = (a { bp_settled_value = bp_settled_value a + x }, x)
132116 flow1 r' a = (a { bp_flow_rate = r' }, r')
133117
134- instance (MonetaryTypes mt , t ~ MT_TIME mt , v ~ MT_VALUE mt , u ~ MT_UNIT mt ) =>
135- MonetaryParticle mt t v u (BasicParticle mt ) where
136- align2 u u' (b, a) = (b', a')
137- where r = flowRate b
138- (r', er') = if u' == 0 then (0 , r `mt_v_mul_u` u) else r `mt_v_mul_u_qr_u` (u, u')
139- b' = fst . flow1 r' $ b
140- a' = fst . flow1 (er' + flowRate a) $ a
141-
142- ------------------------------------------------------------------------------------------------------------------------
143- -- Univeral Index
144- ------------------------------------------------------------------------------------------------------------------------
145-
146- -- | A newtype wrapper of an underlying monetary unit @wp@, with a parameterized @mt@.
147- newtype UniversalIndex mt wp = UniversalIndex wp
148-
149- deriving newtype instance (MonetaryTypes mt , Eq wp ) => Eq (UniversalIndex mt wp )
150- deriving newtype instance (MonetaryTypes mt , Semigroup wp ) => Semigroup (UniversalIndex mt wp )
151- deriving newtype instance (MonetaryTypes mt , Monoid wp ) => Monoid (UniversalIndex mt wp )
152- deriving newtype instance MonetaryUnit mt t v wp => MonetaryUnit mt t v (UniversalIndex mt wp )
153- deriving newtype instance IndexedValue mt t v u wp => IndexedValue mt t v u (UniversalIndex mt wp )
154- deriving newtype instance MonetaryParticle mt t v u wp => MonetaryParticle mt t v u (UniversalIndex mt wp )
155-
156118------------------------------------------------------------------------------------------------------------------------
157119-- Proportional Distribution Pool (PDP)
158120------------------------------------------------------------------------------------------------------------------------
@@ -172,27 +134,22 @@ type PDP_MemberMU mt wp = (PDP_Index mt wp, PDP_Member mt wp)
172134
173135pdp_UpdateMember2 ::
174136 ( IndexedValue mt t v u a
175- , MonetaryParticle mt t v u wp
137+ , IndexedValue mt t v u wp
176138 , mu ~ PDP_MemberMU mt wp
177139 ) =>
178140 u -> t -> (a , mu ) -> (a , mu )
179141pdp_UpdateMember2 u' t' (a, (b, pm)) = (a'', (b'', pm''))
180142 where (PDP_Index tu mpi, pm'@ (PDP_Member u _ _)) = settle t' (b, pm)
181143 tu' = tu + u' - u
182- (mpi', a'') = align2 tu tu' (mpi, settle t' a)
144+ (mpi', a'') = align2b tu tu' (mpi, settle t' a)
183145 b'' = PDP_Index tu' mpi'
184146 pm'' = pm' { pdpm_owned_unit = u', pdpm_synced_wp = mpi' }
185147
186148--
187149-- PDP_Index as MonetaryIndex
188150--
189151
190- instance MonetaryUnit mt t v wp =>
191- MonetaryUnit mt t v (PDP_Index mt wp ) where
192- settle t' a@ (PDP_Index _ mpi) = a { pdpi_wp = settle t' mpi }
193- settledAt (PDP_Index _ mpi) = settledAt mpi
194- flowRate (PDP_Index _ mpi) = flowRate mpi
195- rtb (PDP_Index _ mpi) = rtb mpi
152+ deriving instance (MonetaryTypes mt , Eq wp ) => Eq (PDP_Index mt wp )
196153
197154instance (MonetaryTypes mt , Semigroup wp ) => Semigroup (PDP_Index mt wp ) where
198155 -- The binary operator supports negative unit values while abiding the monoidal laws.
@@ -203,7 +160,14 @@ instance (MonetaryTypes mt, Semigroup wp) => Semigroup (PDP_Index mt wp) where
203160instance (MonetaryTypes mt , Monoid wp ) => Monoid (PDP_Index mt wp ) where
204161 mempty = PDP_Index 0 mempty
205162
206- instance MonetaryParticle mt t v u wp =>
163+ instance MonetaryUnit mt t v wp =>
164+ MonetaryUnit mt t v (PDP_Index mt wp ) where
165+ settle t' a@ (PDP_Index _ mpi) = a { pdpi_wp = settle t' mpi }
166+ settledAt (PDP_Index _ mpi) = settledAt mpi
167+ flowRate (PDP_Index _ mpi) = flowRate mpi
168+ rtb (PDP_Index _ mpi) = rtb mpi
169+
170+ instance IndexedValue mt t v u wp =>
207171 IndexedValue mt t v u (PDP_Index mt wp ) where
208172 shift1 x a@ (PDP_Index tu mpi) = (a { pdpi_wp = mpi' }, x' `mt_v_mul_u` tu)
209173 where (mpi', x') = if tu == 0 then (mpi, 0 ) else shift1 (x `mt_v_div_u` tu) mpi
@@ -212,13 +176,19 @@ instance MonetaryParticle mt t v u wp =>
212176 where (mpi', r'') = if tu == 0 then flow1 0 mpi else flow1 (r' `mt_v_div_u` tu) mpi
213177
214178--
215- -- PDP_MemberMU as MonetaryUnit
179+ -- PDP_Member
216180--
217181
218182instance (MonetaryTypes mt , Monoid wp ) =>
219183 Default (PDP_Member mt wp ) where
220184 def = PDP_Member 0 0 mempty
221185
186+ deriving instance (MonetaryTypes mt , Eq wp ) => Eq (PDP_Member mt wp )
187+
188+ --
189+ -- PDP_MemberMU as MonetaryUnit
190+ --
191+
222192instance MonetaryUnit mt t v wp =>
223193 MonetaryUnit mt t v (PDP_MemberMU mt wp ) where
224194 settle t' (pix, pm) = (pix', pm')
0 commit comments