packages feed

named-records 0.2.3 → 0.3

raw patch · 2 files changed

+184/−77 lines, 2 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Data.NamedRecord: (~>) :: RecordTemplate a b c => a -> b -> c
- Data.NamedRecord: class RecordTemplate a b c | a b -> c
- Data.NamedRecord: instance [overlap ok] (ToType v, ToExp d) => RecordTemplate ((String := v) := d) [(String, Q Type, Maybe (Q Exp))] [(String, Q Type, Maybe (Q Exp))]
- Data.NamedRecord: instance [overlap ok] (ToType v, ToExp d) => RecordTemplate Record ((String := v) := d) (Q [Dec])
- Data.NamedRecord: instance [overlap ok] (ToType v, ToType w) => RecordTemplate (String := v) (String := w) [(String, Q Type, Maybe (Q Exp))]
- Data.NamedRecord: instance [overlap ok] (ToType v, ToType w, ToExp d) => RecordTemplate ((String := v) := d) (String := w) [(String, Q Type, Maybe (Q Exp))]
- Data.NamedRecord: instance [overlap ok] (ToType v, ToType w, ToExp d, ToExp e) => RecordTemplate ((String := v) := d) ((String := w) := e) [(String, Q Type, Maybe (Q Exp))]
- Data.NamedRecord: instance [overlap ok] (ToType v, ToType w, ToExp e) => RecordTemplate (String := v) ((String := w) := e) [(String, Q Type, Maybe (Q Exp))]
- Data.NamedRecord: instance [overlap ok] ToType (Q Type)
- Data.NamedRecord: instance [overlap ok] ToType Name
- Data.NamedRecord: instance [overlap ok] ToType v => RecordTemplate (String := v) [(String, Q Type, Maybe (Q Exp))] [(String, Q Type, Maybe (Q Exp))]
- Data.NamedRecord: instance [overlap ok] ToType v => RecordTemplate Record (String := v) (Q [Dec])
+ Data.NamedRecord: extends :: RecordExtends a => Record -> a -> Record
+ Data.NamedRecord: instance [overlap ok] (Data a, Typeable a) => Field a
+ Data.NamedRecord: instance [overlap ok] (Field v, Field w) => RecordTemplate (String := v) (String := w) [(String, Q Type, Maybe (Q Exp))]
+ Data.NamedRecord: instance [overlap ok] (Show n, FieldNames r) => FieldNames ((n := ns) :+ r)
+ Data.NamedRecord: instance [overlap ok] Field (Q Type)
+ Data.NamedRecord: instance [overlap ok] Field Name
+ Data.NamedRecord: instance [overlap ok] Field v => RecordTemplate (String := v) [(String, Q Type, Maybe (Q Exp))] [(String, Q Type, Maybe (Q Exp))]
+ Data.NamedRecord: instance [overlap ok] Field v => RecordTemplate Record (String := v) (Q [Dec])
+ Data.NamedRecord: instance [overlap ok] RecordExtends [(String, (Type, Exp))]
+ Data.NamedRecord: instance [overlap ok] Show n => FieldNames (n := ns)
+ Data.NamedRecord: instance [overlap ok] ToExp e => Field (Name := e)
+ Data.NamedRecord: instance [overlap ok] ToExp e => Field (Q Type := e)

Files

named-records.cabal view
@@ -1,5 +1,5 @@ Name:           named-records-Version:        0.2.3+Version:        0.3 Synopsis:       Flexible records with named fields. Description:    Flexible records with named fields.                 .@@ -15,6 +15,8 @@                 [@v0.2.3@] Cabal package tidied up and basic                     documentation added (also fixed a bug regarding                     definition of default values).+                .+                [@v0.3@] Records can now extend other records.                  License:        MIT License-File:   LICENSE@@ -33,7 +35,7 @@ Source-Repository head     type: darcs     location: hub.darcs.net:names-    tag: v0.2.3+    tag: v0.3   Library
src/Data/NamedRecord.hs view
@@ -107,6 +107,44 @@ >                            := [e| \x -> answer x |] >     `has` "config" := ''Config := [e| newConfig |] +It is furthermore possible to extend existing records+(but due to stage restrictions in GHCs implementation of+Template Haskell, two records of which one extends the other+can not be contained in the same module):++> module Sample2 where+> +> import qualified Data.Name+> import Data.NamedRecord+> import Data.Word+> +> record "Account"+>     `has` "id"        := ''Word64+> +>     `has` "loginName" := ''String+>     `has` "password"  := ''String+> +> record "Person"+>     `has` "id"        := ''Word64+> +>     `has` "firstName" := ''String+>     `has` "lastName"  := ''String++> module Sample where+> +> import qualified Data.Name+> import Data.NamedRecord+> import Data.Word+> +> import Sample2+> +> record "User"+>     `extends` __Person+>     `extends` __Account+> +>     `has` "id"           := ''Word64+>     `has` "emailAddress" := ''String+ -} module Data.NamedRecord (     Property (get, set),@@ -121,28 +159,33 @@     -- See the examples.     record, +    extends,+     -- | Declares a field of a record. Use as infix operators.     -- See the examples.     has, -    RecordTemplate (..),-     -- ** Names     -- For convenience, this module re-exports name TH name functions.     name, nameT, nameV ) where ++import Control.Monad+import Data.Data+import Data.Function (on) import Data.List import qualified Data.Name import Data.Name (name, nameT, nameV)+import Data.Typeable import Language.Haskell.TH+import Language.Haskell.TH.Quote import Language.Haskell.TH.Syntax (Lift (..))  - data a := b = a := b deriving Show -infixl 3 :=+infixr 3 :=   data a :+ b = a :+ b deriving Show@@ -151,7 +194,6 @@   class Property o n v | o n -> v where-         get :: o -> n -> v     set :: o -> n := v -> o @@ -166,128 +208,152 @@   instance Property (n := v) n v where-     get (_ := v) _ = v     set _ v = v - instance Property ((n := v) :+ b) n v where-     get (a :+ b) n = get a n     set (a :+ b) p = (set a p) :+ b - instance Property b n v => Property (a :+ b) n v where-     get (a :+ b) n = get b n     set (a :+ b) p = a :+ (set b p)  +class ToExp a where+    toExp' :: a -> Q Exp -data Record = Record String+instance ToExp (Q Exp) where+    toExp' = id -record :: String -> Record-record = Record+instance Lift a => ToExp a where+    toExp' = lift  -class ToType a where toType :: a -> Q Type+class Field a where+    toExp :: a -> Maybe (Q Exp)+    toType :: a -> Q Type -instance ToType (Q Type) where toType = id-instance ToType Name where toType = return . ConT+    toExp _ = Nothing +instance ToExp e => Field (Q Type := e) where+    toExp (_ := e) = Just $ toExp' e+    toType (v := _) = v -class ToExp a where toExp :: a -> Q Exp+instance ToExp e => Field (Name := e) where+    toExp (_ := e) = Just $ toExp' e+    toType (v := _) = return $ ConT v -instance ToExp (Q Exp) where toExp = id-instance Lift a => ToExp a where toExp = lift+instance Field Name where+    toType = return . ConT +instance Field (Q Type) where+    toType = id +instance (Data a, Typeable a) => Field a where+    toExp = Just . dataToExpQ (const Nothing)+    toType e = qType typeName+      where+        typeName = show (typeOf e)++        qType typeName = case typeName of++            -- This is a dirty hack. It would be much better+            -- to resolve all type synonyms to their real types.+            -- This would have to be done in (~>)+            "[Char]" -> return $ ConT ''String++            ('[':xs) -> do+                t <- qType (init xs)+                return $ AppT ListT t++            ('M':'a':'y':'b':'e':' ':xs) -> do+                t <- qType xs+                m <- [t| Maybe |]+                return $ AppT m t++            name -> lookupTypeName name >>= typeFor++        typeFor (Just n) = return $ ConT n+        typeFor Nothing = fail $ "The type \"" ++ typeName+            ++ "\" can not be handled by Data.NamedRecord TH sugar"+            ++ " (you might to import the module where it's from)."++ class RecordTemplate a b c | a b -> c where     (~>) :: a -> b -> c --instance (ToType v, ToType w) => RecordTemplate+instance (Field v, Field w) => RecordTemplate         (String := v)         (String := w)         [(String, Q Type, Maybe (Q Exp))] where-    (n := v) ~> (m := w) = [(n, toType v, Nothing),-                            (m, toType w, Nothing)]+    (n := v) ~> (m := w) = [(n, toType v, toExp v),+                            (m, toType w, toExp w)] -instance (ToType v, ToType w, ToExp e) => RecordTemplate+instance Field v => RecordTemplate         (String := v)-        (String := w := e)+        [(String, Q Type, Maybe (Q Exp))]         [(String, Q Type, Maybe (Q Exp))] where-    (n := v) ~> (m := w := e) = [(n, toType v, Nothing),-                                 (m, toType w, Just $ toExp e)]+    (n := v) ~> xs = (n, toType v, toExp v) : xs -instance (ToType v, ToType w, ToExp d) => RecordTemplate-        (String := v := d)-        (String := w)-        [(String, Q Type, Maybe (Q Exp))] where-    (n := v := d) ~> (m := w) = [(n, toType v, Just $ toExp d),-                                 (m, toType w, Nothing)]+instance Field v => RecordTemplate Record (String := v) (Q [Dec]) where+    r ~> (n := v) = r ~> [(n, toType v, toExp v)] -instance (ToType v, ToType w, ToExp d, ToExp e) => RecordTemplate-        (String := v := d)-        (String := w := e)-        [(String, Q Type, Maybe (Q Exp))] where-    (n := v := d) ~> (m := w := e) = [(n, toType v, Just $ toExp d),-                                      (m, toType w, Just $ toExp e)]+instance RecordTemplate+        Record [(String, Q Type, Maybe (Q Exp))] (Q [Dec]) where -instance ToType v => RecordTemplate-        (String := v)-        [(String, Q Type, Maybe (Q Exp))]-        [(String, Q Type, Maybe (Q Exp))] where-    (n := v) ~> xs = (n, toType v, Nothing) : xs+    Record name xs ~> fs = do+        let typeD typ = TySynD (mkName name) [] typ -instance (ToType v, ToExp d) => RecordTemplate-        (String := v := d)-        [(String, Q Type, Maybe (Q Exp))]-        [(String, Q Type, Maybe (Q Exp))] where-    (n := v := d) ~> xs = (n, toType v, Just $ toExp d) : xs+            noValue = VarE 'value +            normalize (n, v, d) = do+                v' <- v+                d' <- maybe (return noValue) id d+                return (n, (v', d')) -instance ToType v => RecordTemplate Record (String := v) (Q [Dec]) where-    r ~> (n := v) = r ~> [(n, toType v, Nothing :: Maybe (Q Exp))]+        nFields <- mapM normalize fs+                    >>= return . sortBy (compare `on` fst) -instance (ToType v, ToExp d) =>-        RecordTemplate Record (String := v := d) (Q [Dec]) where-    r ~> (n := v := d) = r ~> [(n, toType v, Just $ toExp d)]+        let fs' = sortBy (compare `on` fst) (concat (nFields : xs)) +            unify sss@(s:ss) =+                if all id (zipWith ((==) `on` (fst . snd)) ss sss)+                    then return $ select sss+                    else fail $ "Types for the named field \""+                        ++ fst s ++ "\" could not be unified."+                        ++ "\n    The conflicting types are: \n"+                        ++ unlines (map (show . fst . snd) sss) -instance RecordTemplate-        Record [(String, Q Type, Maybe (Q Exp))] (Q [Dec]) where+            select (x:[]) = x+            select (x@(_, (_, d)) : xs)+                | d == noValue = select xs+                | otherwise    = x -    Record name ~> fs = do-        let typeD typ = TySynD (mkName name) [] typ+            mkDef (n, (v, d)) = do+                name <- nameT n+                return (AppT (AppT (ConT ''(:=)) name) v, d) -            func (name, valType, defaultVal) = do-                nameType <- nameT name-                valueType <- valType-                defaultValue <- maybe (return $ VarE 'value) id defaultVal-                return $ ( AppT (AppT (ConT ''(:=)) nameType) valueType-                         , defaultValue )+        nFields <- mapM unify (groupBy ((==) `on` fst) fs')+        fields  <- mapM mkDef nFields+        rExp    <- dataToExpQ (const Nothing) nFields -        fields <- mapM func-            $ sortBy (\(x, _, _) (y, _, _) -> compare x y) fs+        let reflD = ValD (VarP (mkName ("__" ++ name))) (NormalB rExp) [] -        let syn = foldr (\(x, _) xs -> AppT (AppT (ConT ''(:+)) x) xs)+            syn = foldr (\(x, _) xs -> AppT (AppT (ConT ''(:+)) x) xs)                         (fst $ last fields) (init fields)              cName = mkName ("new" ++ name)--            sigD = SigD cName (ConT (mkName name))-+            sigD  = SigD cName (ConT (mkName name))             funcD = ValD (VarP cName) (NormalB funcB) []             funcB = foldr join (field $ last fields) (init fields)               where                 join x xs = InfixE (Just $ field x) (ConE '(:+)) (Just xs)--            field (_, x) = InfixE (Just (VarE '_type))-                             (ConE '(:=))-                             (Just x)+                field (_, x) = InfixE (Just (VarE '_type))+                                      (ConE '(:=))+                                      (Just x) -        return [typeD syn, sigD, funcD]+        return [typeD syn, sigD, funcD, reflD]   _type = error $ "NamedRecord field type unwrapped!"@@ -301,5 +367,44 @@  infixr 1 ~> infixr 1 `has`+++class RecordExtends a where+    (<:) :: Record -> a -> Record++instance RecordExtends [(String, (Type, Exp))] where+    Record name xs <: x = Record name (x:xs)++extends :: RecordExtends a => Record -> a -> Record+extends = (<:)++infixl 2 <:+infixl 2 `extends`+++data Record = Record String [[(String, (Type, Exp))]]++record :: String -> Record+record name = Record name []+++-- Playground below++fieldNames :: FieldNames a => a -> [String]+fieldNames = _fieldNames []+++class FieldNames a where+    _fieldNames :: [String] -> a -> [String]++instance (Show n, FieldNames r) =>+        FieldNames (n := ns :+ r) where++    _fieldNames xs (n := _ :+ r) = _fieldNames (show n : xs) r++instance (Show n) => FieldNames (n := ns) where++    _fieldNames xs (n := _) = show n : xs+