| 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 DORFCRM where |
|---|
| 7 | > |
|---|
| 8 | > import DORF hiding (lastName, firstName, Proxy_lastName, Proxy_firstName) |
|---|
| 9 | > -- Note: import is _not_ qualified |
|---|
| 10 | |
|---|
| 11 | |
|---|
| 12 | ------------------ fieldLabel decls (for 'monomorphic' fields) ------------------------------------- |
|---|
| 13 | |
|---|
| 14 | -- fieldLabel customer_id :: r -> Int -- already decl'd in Module DORF |
|---|
| 15 | -- so we're going to share it |
|---|
| 16 | |
|---|
| 17 | |
|---|
| 18 | fieldLabel firstName :: r -> String -- same Label as in the imported module, but we've hidden the import |
|---|
| 19 | |
|---|
| 20 | > data Proxy_firstName |
|---|
| 21 | > -- firstName :: (Has r Proxy_firstName t, t ~ String) => r -> String |
|---|
| 22 | > firstName r = get r (undefined :: Proxy_firstName) |
|---|
| 23 | > |
|---|
| 24 | > type instance GetResult r Proxy_firstName t = String -- firstName always a String, for all record types |
|---|
| 25 | > |
|---|
| 26 | |
|---|
| 27 | fieldLabel lastName :: r -> String -- also same label as in imported DORF |
|---|
| 28 | |
|---|
| 29 | > data Proxy_lastName |
|---|
| 30 | > -- lastName :: (Has r Proxy_lastName t, t ~ String) => r -> String |
|---|
| 31 | > lastName r = get r (undefined :: Proxy_lastName) |
|---|
| 32 | > |
|---|
| 33 | > type instance GetResult r Proxy_lastName t = String -- firstName always a String, for all record types |
|---|
| 34 | |
|---|
| 35 | |
|---|
| 36 | ------------------Virtual field definition (note no Has instance needed, so no need to worry about set) ---- |
|---|
| 37 | |
|---|
| 38 | > -- fullName r = firstName r ++ " " ++ lastName r -- is imported from DORF |
|---|
| 39 | > -- but defined only for DORF.firstName, DORF.lastName |
|---|
| 40 | |
|---|
| 41 | |
|---|
| 42 | |
|---|
| 43 | ------------------Record decl, and Has instance generated (for 'monomorphic' fields)---------------------------- |
|---|
| 44 | |
|---|
| 45 | > data Customer_Contact = Cust_Cont { customer_id____ :: Int, firstName__, lastName__ :: String } |
|---|
| 46 | > deriving (Eq, Show, Read) |
|---|
| 47 | > -- suffixing the field name to avoid clash |
|---|
| 48 | > |
|---|
| 49 | > type instance SetResult Customer_Contact fld t = Customer_Contact |
|---|
| 50 | > -- Customer_Contact has no type params |
|---|
| 51 | > |
|---|
| 52 | > -- note: GetResult type instances already decl'd, by field |
|---|
| 53 | > |
|---|
| 54 | > instance (t ~ Int) => Has Customer_Contact Proxy_customer_id t where -- using imported customer_id |
|---|
| 55 | > get Cust_Cont{customer_id____} _ = customer_id____ |
|---|
| 56 | > set _ x Cust_Cont{..} = Cust_Cont{customer_id____ = x, .. } |
|---|
| 57 | > |
|---|
| 58 | > instance (t ~ String) => Has Customer_Contact Proxy_firstName t where -- using local firstName |
|---|
| 59 | > get Cust_Cont{firstName__} _ = firstName__ |
|---|
| 60 | > set _ x Cust_Cont{..} = Cust_Cont{firstName__ = x, .. } |
|---|
| 61 | > |
|---|
| 62 | > instance (t ~ String) => Has Customer_Contact Proxy_lastName t where -- using local lastName |
|---|
| 63 | > get Cust_Cont{lastName__} _ = lastName__ |
|---|
| 64 | > set _ x Cust_Cont{..} = Cust_Cont{lastName__ = x, .. } |
|---|
| 65 | > |
|---|
| 66 | |
|---|
| 67 | ----------------- tests --------------------------------------------------------------------------------------- |
|---|
| 68 | |
|---|
| 69 | > rCC1 = Cust_Cont{ customer_id____ = 57, firstName__ = "Fred", lastName__ = "Daggy" } |
|---|
| 70 | > |
|---|
| 71 | > -- customer_id rCC1 ==> 57 -- sharing customer_id |
|---|
| 72 | > -- firstName rCC1 ++ " " ++ lastName tCC1 ==> "Fred Daggy" |
|---|
| 73 | > |
|---|
| 74 | > -- lastName rCN0 ==> No instance for (Has Customer_NameAddress Proxy_lastName t0) |
|---|
| 75 | > -- arising from a use of `lastName' |
|---|
| 76 | > -- DORF.lastName rCC1 ==> No instance for (Has Customer_Contact DORF.Proxy_lastName t0) |
|---|
| 77 | > -- arising from a use of `DORF.lastName' |
|---|
| 78 | > |
|---|
| 79 | > -- fullName rCC1 ==> No instances for (Has Customer_Contact DORF.Proxy_firstName t0, |
|---|
| 80 | > -- Has Customer_Contact DORF.Proxy_lastName t10) |
|---|
| 81 | > -- arising from a use of `fullName' |
|---|
| 82 | > |
|---|
| 83 | |
|---|
| 84 | To explain that failure on fullName: |
|---|
| 85 | We imported fullName, defined against the fieldLabel proxys in Module DORF. |
|---|
| 86 | And we imported customer_id defined similarly, |
|---|
| 87 | instanced Customer_contact against the shared Proxy_customer_id |
|---|
| 88 | But we defined local versions of the name fieldLabels, |
|---|
| 89 | so fullName wasn't instanced against them. |
|---|
| 90 | |
|---|
| 91 | This shows that within a single record decl: |
|---|
| 92 | 1. You can create fields that share Labels with imports. |
|---|
| 93 | 2. You can create fields that don't share, even with the same Label name. |
|---|
| 94 | (That is, the module system continues to control the namespace.) |
|---|
| 95 | 3. You can prevent using the wrong field selector with the wrong record type, |
|---|
| 96 | even if they have the same Label name. |
|---|
| 97 | |
|---|