| 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 | > |
|---|
| 8 | > import 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 | |
|---|
| 16 | here's the mechanism -- declarations for Has/get/set, and type families |
|---|
| 17 | |
|---|
| 18 | > class 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 | > |
|---|
| 23 | > type family GetResult r fld t :: * -- type of t get from r at fld, or to set in set's result |
|---|
| 24 | > type 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 | |
|---|
| 36 | > data 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 |
|---|
| 39 | > customer_id r = get r (undefined :: Proxy_customer_id) |
|---|
| 40 | > |
|---|
| 41 | > type 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 | |
|---|
| 48 | > data Proxy_firstName |
|---|
| 49 | > -- firstName :: (Has r Proxy_firstName t, t ~ String) => r -> String |
|---|
| 50 | > firstName r = get r (undefined :: Proxy_firstName) |
|---|
| 51 | > |
|---|
| 52 | > type instance GetResult r Proxy_firstName t = String -- firstName always a String, for all record types |
|---|
| 53 | > |
|---|
| 54 | |
|---|
| 55 | fieldLabel lastName :: r -> String |
|---|
| 56 | |
|---|
| 57 | > data Proxy_lastName |
|---|
| 58 | > -- lastName :: (Has r Proxy_lastName t, t ~ String) => r -> String |
|---|
| 59 | > lastName r = get r (undefined :: Proxy_lastName) |
|---|
| 60 | > |
|---|
| 61 | > type 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 | |
|---|
| 66 | > fullName 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 | |
|---|
| 76 | > data Customer_NameAddress = Cust_NA { customer_id_ :: Int, firstName_, lastName_ :: String } |
|---|
| 77 | > deriving (Eq, Show, Read) |
|---|
| 78 | > -- suffixing the field name to avoid clash |
|---|
| 79 | > |
|---|
| 80 | > type 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 | > |
|---|
| 85 | > instance (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 | > |
|---|
| 89 | > instance (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 | > |
|---|
| 93 | > instance (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 | |
|---|
| 103 | > data Proxy_unitPrice |
|---|
| 104 | > -- unitPrice :: (Has r Proxy_unitPrice t, Num t) => r -> t |
|---|
| 105 | > unitPrice r = get r (undefined :: Proxy_unitPrice) |
|---|
| 106 | > |
|---|
| 107 | > -- Note: no type instance for GetResult because may vary per record type |
|---|
| 108 | > |
|---|
| 109 | > data Customer_Price a = Num a => Cust_Price { customer_id__ :: Int, |
|---|
| 110 | > product_id__ :: Int, |
|---|
| 111 | > unitPrice_ :: a } -- polymorphic (constrained) |
|---|
| 112 | > |
|---|
| 113 | > instance (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 | > |
|---|
| 117 | > type instance SetResult (Customer_Price a) Proxy_customer_id t = Customer_Price a |
|---|
| 118 | > |
|---|
| 119 | > |
|---|
| 120 | > type instance GetResult (Customer_Price a) Proxy_unitPrice t = a -- field's type is whatever's there |
|---|
| 121 | > type instance SetResult (Customer_Price _a) Proxy_unitPrice t = Customer_Price t -- updating record type per arg to set |
|---|
| 122 | > |
|---|
| 123 | > instance (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 | > |
|---|
| 129 | > rCP0 = Cust_Price{ customer_id__ = 27, product_id__ = 54321, unitPrice_ = (92 :: Num a => a) } |
|---|
| 130 | > -- ?? but GHCi :: Integer |
|---|
| 131 | > rCP1 = set (undefined :: Proxy_unitPrice) (131.7) rCP0 |
|---|
| 132 | > -- ?? but GHCi :: Double |
|---|
| 133 | > up1 = unitPrice rCP1 -- ==> 131.7 :: Double |
|---|
| 134 | > |
|---|
| 135 | > rCN0 = 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 | |
|---|
| 146 | > data Proxy_rev -- phantom, same role as SPJ's String Kind "rev" |
|---|
| 147 | > rev :: Has r Proxy_rev t => r -> t -- is type inferred |
|---|
| 148 | > rev r = get r (undefined :: Proxy_rev) |
|---|
| 149 | > type instance GetResult r Proxy_rev t = t -- field's type is whatever's there (it's opaque) |
|---|
| 150 | > type instance SetResult HR Proxy_rev t = HR -- the H-R type is hidded inside HR |
|---|
| 151 | |
|---|
| 152 | > instance (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 | > |
|---|
| 157 | > setHRrev :: fld -> (forall a_. [a_] -> [a_]) -> HR -> HR |
|---|
| 158 | > setHRrev _fld fn HR{..} = HR{ rev_ = fn, ..} |
|---|
| 159 | > |
|---|
| 160 | > |
|---|
| 161 | > testrev r = (rev r [True, False, False], rev r "hello") |
|---|
| 162 | > |
|---|
| 163 | > rHR0 = HR{} -- field `rev_' is undefined, to show we can update it |
|---|
| 164 | > rHR1 = setHRrev (undefined :: Proxy_rev) reverse rHR0 -- equivalent to rHR0{rev = reverse} |
|---|
| 165 | > -- testrev rHR1 ==> ([False,False,True],"olleh") |
|---|
| 166 | > rHR2 = 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 | |
|---|
| 173 | > data 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 | |
|---|
| 182 | > data Proxy_tag |
|---|
| 183 | > tag r = get r (undefined :: Proxy_tag) |
|---|
| 184 | > type instance GetResult r Proxy_tag t = String |
|---|
| 185 | > instance (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 | > |
|---|
| 191 | > type instance SetResult (Tab a b) Proxy_tag t = Tab a b |
|---|
| 192 | |
|---|
| 193 | |
|---|
| 194 | > instance (t ~ ([a_]->[a_])) => Has (Tab a b) Proxy_rev t where |
|---|
| 195 | > get Ta{rev_} _ = rev_ |
|---|
| 196 | > -- set _ fn Ta{..} = Ta{rev_ = fn, ..} |
|---|
| 197 | > |
|---|
| 198 | > type 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 | |
|---|
| 203 | > data Proxy_flda -- `flda's type is a parameter to data type (Tab a b) |
|---|
| 204 | > flda r = get r (undefined :: Proxy_flda) |
|---|
| 205 | |
|---|
| 206 | > instance 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 | > |
|---|
| 211 | > type instance GetResult (Tab a b) Proxy_flda t = a -- result is what comes from Tab |
|---|
| 212 | > type instance SetResult (Tab a b) Proxy_flda t = Tab t b -- type to set is whatever we're given |
|---|
| 213 | > |
|---|
| 214 | |
|---|
| 215 | > setTabrev :: fld -> (forall a_. [a_] -> [a_]) -> Tab a b -> Tab a b |
|---|
| 216 | > setTabrev _fld fn Ta{..} = Ta{ rev_ = fn, ..} |
|---|
| 217 | > |
|---|
| 218 | > rTab0 = Ta{tag_ = "tagged"} -- `rev_' is undefined |
|---|
| 219 | > rTab1 = setTabrev (undefined :: Proxy_rev) reverse rTab0 |
|---|
| 220 | > -- testrev rTab1 ==> ([False,False,True],"olleh") |
|---|
| 221 | > rTab2 = 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 | |
|---|
| 229 | > data Proxy_fldn -- } the Existential fields must be set together |
|---|
| 230 | > data Proxy_fldnb -- } possible approach for multiple update |
|---|
| 231 | > fldn r = get r (undefined :: Proxy_fldn) |
|---|
| 232 | > fldnb 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 | |
|---|
| 240 | > instance (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 | > |
|---|
| 248 | > type instance GetResult (Tab a b) (Proxy_fldn, Proxy_fldnb) t = b |
|---|
| 249 | > type instance SetResult (Tab a b) (Proxy_fldn, Proxy_fldnb) (tn, tn -> b') = Tab a b' |
|---|
| 250 | > |
|---|
| 251 | > nb' 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 |
|---|
| 254 | > rTab3 = Tb{tag_ = "tagged", fldn_ = 6 :: Double, fldnb_ = negate} |
|---|
| 255 | > -- nb' rTab3 ==> -6.0 :: Double |
|---|
| 256 | > rTab4 = set (undefined :: (Proxy_fldn, Proxy_fldnb)) (5::Int, (6 +)) rTab3 |
|---|
| 257 | > -- rTab3{fldn = 5, fldnb = (6 +)} |
|---|
| 258 | > -- nb' rTab4 ==> 11 :: Int |
|---|
| 259 | |
|---|