Records/DeclaredOverloadedRecordFields/ImplementorsView: DORF Prototype ADC5 15Feb2012.lhs

File DORF Prototype ADC5 15Feb2012.lhs, 11.9 KB (added by guest, 15 months ago)

Prototype implementation

Line 
1> {-# OPTIONS_GHC -XDisambiguateRecordFields -XNamedFieldPuns -XRecordWildCards #-}
2> {-# OPTIONS_GHC -XTypeFamilies        -XFlexibleContexts                      #-}
3> {-# OPTIONS_GHC -XRankNTypes  -XImpredicativeTypes -XGADTs -XEmptyDataDecls   #-}
4> {-# OPTIONS_GHC -XMultiParamTypeClasses -XFlexibleInstances -XUndecidableInstances   #-}
5
6> module DORF   where
7>
8import HRrev_
9
10                     -- SPJ's example of a higher-ranked data type
11                     -- imported so that we have clashing declarations of `rev' -- see below
12
13
14--------------- The Has class, methods get and set -----------------------------------------
15
16here's the mechanism -- declarations for Has/get/set, and type families
17
18class Has r fld t    where
19>    get        :: r -> fld -> GetResult r fld t           -- result is t in the simple cases
20>    set        :: (Has (SetResult r fld t) fld t) => fld -> t -> r -> SetResult r fld t  -- result is r in the simple cases
21> --   set        :: fld -> t -> r -> SetResult r fld t      -- result is r in the simple cases
22>
23type family GetResult  r fld t   :: *             -- type of t get from r at fld, or to set in set's result
24type family SetResult  r fld t   :: *             -- type of r updated with t
25
26
27   Using type family rather than associated type, because:
28   * often the same GetResult for a given fld (all records) -- such as non-changeable fields, see next
29   * often the same SetResult for a given record (all fields) -- such as non-changeable records
30
31
32------------------ fieldLabel decls (for 'monomorphic' fields) -------------------------------------
33
34   fieldLabel customer_id :: r -> Int
35
36data Proxy_customer_id
37-- customer_id :: (Has r Proxy_customer_id t, t ~ Int) => r -> t  -- GHC can't infer this
38-- customer_id :: (Has r Proxy_customer_id t) => r -> Int      -- type inferred, but can't verify
39customer_id r = get r (undefined :: Proxy_customer_id)
40>
41type instance GetResult r Proxy_customer_id t = Int     -- customer_id always an Int, for all record types
42>
43-- ?? type instance SetResult r Proxy_customer_id t = r -- changing customer_id doesn't change the record type
44>
45
46   fieldLabel firstName :: r -> String
47
48data Proxy_firstName
49-- firstName :: (Has r Proxy_firstName t, t ~ String) => r -> String
50firstName r = get r (undefined :: Proxy_firstName)
51>
52type instance GetResult r Proxy_firstName t = String    -- firstName always a String, for all record types
53>
54
55   fieldLabel lastName :: r -> String
56
57data Proxy_lastName
58-- lastName :: (Has r Proxy_lastName t, t ~ String) => r -> String
59lastName r = get r (undefined :: Proxy_lastName)
60>
61type instance GetResult r Proxy_lastName t = String    -- firstName always a String, for all record types
62
63
64------------------Virtual field definition (note no Has instance needed, so no need to worry about set) ----
65
66fullName r = firstName r ++ " " ++ lastName r           -- per SPJ, avoiding dot notation
67
68   fullName :: (Has r Proxy_firstName t1, Has r Proxy_lastName t2) => r -> String  -- inferred from def'n
69                                                                                   -- but GHC can't validate
70
71     <===>  :: r{ firstName, lastName :: String } => r -> String
72
73
74------------------Record decl, and Has instance generated (for 'monomorphic' fields)----------------------------
75
76data Customer_NameAddress = Cust_NA { customer_id_ :: Int, firstName_, lastName_ :: String }
77>                              deriving (Eq, Show, Read)
78>                                                    -- suffixing the field name to avoid clash
79>
80type instance SetResult Customer_NameAddress fld t = Customer_NameAddress
81>                                        -- Customer_NameAddress has no type params
82>
83>                                        -- note: GetResult type instances already decl'd, by field
84>
85instance (t ~ Int) => Has Customer_NameAddress Proxy_customer_id t         where
86>      get Cust_NA{customer_id_} _ = customer_id_
87>      set _ x Cust_NA{..}  = Cust_NA{customer_id_ = x, .. }
88>
89instance (t ~ String) => Has Customer_NameAddress Proxy_firstName t         where
90>      get Cust_NA{firstName_} _ = firstName_
91>      set _ x Cust_NA{..}  = Cust_NA{firstName_ = x, .. }
92>
93instance (t ~ String) => Has Customer_NameAddress Proxy_lastName t         where
94>      get Cust_NA{lastName_} _ = lastName_
95>      set _ x Cust_NA{..}  = Cust_NA{lastName_ = x, .. }
96
97
98
99------------------fieldLabel and Record decl for changeable (constrained) field -------------------------------
100
101    fieldLabel unitPrice :: Num t => r -> t                  -- t is constrained/changeable
102
103data Proxy_unitPrice
104-- unitPrice :: (Has r Proxy_unitPrice t, Num t) => r -> t
105unitPrice r = get r (undefined :: Proxy_unitPrice) 
106>
107-- Note: no type instance for GetResult because may vary per record type
108>
109data Customer_Price a = Num a => Cust_Price { customer_id__ :: Int,
110>                                                product_id__  :: Int,
111>                                                unitPrice_    :: a }     -- polymorphic (constrained) 
112>
113instance (t ~ Int) => Has (Customer_Price a) Proxy_customer_id t         where
114>      get Cust_Price{customer_id__} _ = customer_id__
115>      set _ x Cust_Price{..}  = Cust_Price{customer_id__ = x, .. }
116>
117type instance SetResult (Customer_Price a) Proxy_customer_id t = Customer_Price a
118>
119>
120type instance GetResult (Customer_Price  a) Proxy_unitPrice t = a          -- field's type is whatever's there
121type instance SetResult (Customer_Price _a) Proxy_unitPrice t = Customer_Price t      -- updating record type per arg to set
122>
123instance (Num t) => Has (Customer_Price a) Proxy_unitPrice t        where
124>      get Cust_Price{unitPrice_} _ = unitPrice_
125>      set _ x Cust_Price{..} = Cust_Price{ unitPrice_ = x, .. }
126>
127>
128>
129rCP0 = Cust_Price{ customer_id__ = 27, product_id__ = 54321, unitPrice_ = (92 :: Num a => a) }
130>                                                                         -- ?? but GHCi :: Integer
131rCP1 = set (undefined :: Proxy_unitPrice) (131.7) rCP0
132>                                                                         -- ?? but GHCi :: Double
133up1  = unitPrice rCP1                 -- ==> 131.7 :: Double
134>
135rCN0 = Cust_NA{ customer_id_ = 35, firstName_ = "John", lastName_ = "Smith" }
136>
137
138
139---------------------- HR: Higher-ranked type, per SPJ ------------------------------
140
141-- import HRrev_
142{- data HR   = HR {rev_ :: forall a_. [a_] -> [a_]}  -}
143
144   fieldLabel rev :: r -> (forall a_. [a_] -> [a_])
145
146data Proxy_rev              -- phantom, same role as SPJ's String Kind "rev"
147rev :: Has r Proxy_rev t => r -> t                     -- is type inferred
148rev r = get r (undefined :: Proxy_rev)
149type instance GetResult r Proxy_rev t = t          -- field's type is whatever's there (it's opaque)
150type instance SetResult HR Proxy_rev t = HR        -- the H-R type is hidded inside HR
151
152instance (t ~ ([a_] -> [a_])) => Has HR Proxy_rev t           where
153>      get HR{rev_} _ = rev_
154>      -- set _ fn HR{..} = HR{ rev_ = fn, ..}
155>      -- set     = setHRrev                       -- can't match the forall
156>
157setHRrev :: fld -> (forall a_. [a_] -> [a_]) -> HR -> HR
158setHRrev _fld fn HR{..} = HR{ rev_ = fn, ..}
159>
160>
161testrev r = (rev r [True, False, False], rev r "hello")
162>
163rHR0 = HR{}               -- field `rev_' is undefined, to show we can update it
164rHR1 = setHRrev (undefined :: Proxy_rev) reverse rHR0           -- equivalent to rHR0{rev = reverse}
165>                            -- testrev rHR1 ==> ([False,False,True],"olleh")
166rHR2 = setHRrev (undefined :: Proxy_rev) (drop 1 . reverse) rHR1
167>                            -- testrev rHR2 ==> ([False,True],"lleh")
168
169
170
171---------------------- type Tab -- more complex example, including clash of field rev_ ------------------------------
172
173data Tab a b         where   -- a different data type with a HR field `rev'
174>    Ta :: {tag_ :: String, rev_ :: forall a_.([a_] -> [a_]), flda_ :: a }
175>       -> Tab a b
176>    Tb :: (Num n, Show b) => {tag_ :: String, fldn_ :: n, fldnb_ :: n -> b}
177>       -> Tab a b
178>                              -- Existential fields (GADT syntax)
179
180   fieldLabel tag :: r -> String
181
182data Proxy_tag
183tag r = get r (undefined :: Proxy_tag)
184type instance GetResult r Proxy_tag t = String
185instance (t ~ String) => Has (Tab a b) Proxy_tag t   where
186>      get Ta{tag_} _   = tag_
187>      get Tb{tag_} _   = tag_
188>      set _ x   Ta{..} = Ta{tag_ = x, ..}
189>      set _ x   Tb{..} = Tb{tag_ = x, ..}
190>
191type instance SetResult  (Tab a b) Proxy_tag t = Tab a b
192
193
194instance (t ~ ([a_]->[a_])) => Has (Tab a b) Proxy_rev t     where
195>      get Ta{rev_} _   = rev_
196--    set _ fn  Ta{..}       = Ta{rev_ = fn, ..}
197>
198type instance SetResult (Tab a b) Proxy_rev t = Tab a b        -- the H-R type is hidded inside Tab
199
200
201   fieldLabel flda :: r -> t
202
203data Proxy_flda              -- `flda's type is a parameter to data type (Tab a b)
204flda r = get r (undefined :: Proxy_flda)
205
206instance Has (Tab a b) Proxy_flda t  where  -- note constraint on `t',
207>                                      -- might be different to `a' for update
208>    get Ta{flda_} _    = flda_
209>    set _ x   Ta{..}   = Ta{flda_ = x, ..}
210>
211type instance GetResult (Tab a b) Proxy_flda t = a             -- result is what comes from Tab
212type instance SetResult (Tab a b) Proxy_flda t = Tab t b     -- type to set is whatever we're given
213>
214
215setTabrev :: fld -> (forall a_. [a_] -> [a_]) -> Tab a b -> Tab a b
216setTabrev _fld fn Ta{..} = Ta{ rev_ = fn, ..}
217>
218rTab0 = Ta{tag_ = "tagged"}  -- `rev_' is undefined
219rTab1 = setTabrev (undefined :: Proxy_rev) reverse rTab0
220>                            -- testrev rTab1 ==> ([False,False,True],"olleh")
221rTab2 = set (undefined :: Proxy_flda) 'a' (setTabrev (undefined :: Proxy_rev) (reverse . take 3) rTab1)
222>                            -- rTab1 :: Tab a b ; rTab2 :: Tab Char b
223>                            -- testrev rTab2 ==> ([False,False,True],"leh")
224
225
226   fieldLabel fldn :: r -> t
227   fieldLabel fldnb :: r -> t
228
229data Proxy_fldn            -- } the Existential fields must be set together
230data Proxy_fldnb           -- } possible approach for multiple update
231fldn  r = get r (undefined :: Proxy_fldn)
232fldnb r = get r (undefined :: Proxy_fldnb)
233-- no point in a 'getter' function: the types would escape
234
235
236------------------------ get/set 2 fields at the same time ----------------------------
237
238                         possible approach for r{ fldn = ..., fldnb = ... }
239
240instance (t ~ (tn, tn -> b'), Show b', Num tn)
241>                                              -- needs -XUndecidableInstances
242>           => Has (Tab a b) (Proxy_fldn, Proxy_fldnb) t        where
243> -- get Tb{fldn_, fldnb_} _    = (fldn_, fldnb_)       -- No! types would escape
244>    get Tb{fldn_, fldnb_} _    = fldnb_ fldn_          -- all we can do is apply
245>    set _ (n, nb)   Tb{..}     = Tb{fldn_ = n, fldnb_ = nb, ..}
246>                                   -- the use case is: r{fldn = n, fldnb = nb}
247>
248type instance GetResult  (Tab a b) (Proxy_fldn, Proxy_fldnb) t = b
249type instance SetResult  (Tab a b) (Proxy_fldn, Proxy_fldnb) (tn, tn -> b') = Tab a b'
250>
251nb' Tb{fldn_, fldnb_} = fldnb_ fldn_           -- test rig
252-- nb' r = get r (undefined :: (Proxy_fldn, Proxy_fldnb))            -- ambiguous typevar in (Show b') constraint
253>                                                                       -- deriving from use of get
254rTab3 = Tb{tag_ = "tagged", fldn_ = 6 :: Double, fldnb_ = negate}
255>                                             -- nb' rTab3 ==> -6.0 :: Double
256rTab4 = set (undefined :: (Proxy_fldn, Proxy_fldnb)) (5::Int, (6 +)) rTab3
257>                                             -- rTab3{fldn = 5, fldnb = (6 +)}
258>                                             -- nb' rTab4 ==> 11 :: Int
259