/Control/Newtype/TH.hs

http://github.com/mgsloan/newtype-th · Haskell · 151 lines · 84 code · 18 blank · 49 comment · 3 complexity · 4704821b8fa5c7c62c34cacd3184fe6d MD5 · raw file

  1. {-# LANGUAGE TemplateHaskell, TupleSections #-}
  2. -----------------------------------------------------------------------------
  3. -- |
  4. -- Module : Control.Newtype.TH
  5. -- Copyright : Michael Sloan 2011
  6. --
  7. -- Maintainer : Michael Sloan (mgsloan@gmail.com)
  8. -- Portability : unportable
  9. --
  10. -- This module provides a template Haskell based mechanism for deriving
  11. -- instances of the @Newtype@ class, defined in @ Control.Newtype @ in the
  12. -- newtype package. Example usage:
  13. --
  14. -- > newtype CartesianList a = CartesianList [a]
  15. -- > $(mkNewTypes [''CartesianList])
  16. -- >
  17. -- > instance Monoid (CartesianList a) where
  18. -- > mempty = pack [[]]
  19. -- > a `mappend` b = pack [x ++ y | x <- unpack a, y <- unpack b]
  20. --
  21. -- > *Main> print $ underF CartesianList (\xs -> [fold xs]) ([[[4],[5],[6]], [[1],[2]], [[0]]])
  22. -- > [[[4,1,0],[4,2,0],[5,1,0],[5,2,0],[6,1,0],[6,2,0]]]
  23. --
  24. -----------------------------------------------------------------------------
  25. module Control.Newtype.TH
  26. ( mkNewtype, mkNewtypes
  27. , mkNewType, mkNewTypes ) where
  28. import Control.Newtype ( Newtype(pack, unpack) )
  29. import Control.Applicative ((<$>))
  30. import Control.Arrow ((&&&))
  31. import Data.Function ( on )
  32. import Data.List ( groupBy, sortBy, find, nub )
  33. import Data.Maybe ( catMaybes )
  34. import Data.Ord ( comparing )
  35. import Data.Generics ( Data(gmapQ) )
  36. import Data.Generics.Schemes ( everywhere' )
  37. import Data.Generics.Aliases ( extT, extQ )
  38. import Language.Haskell.TH
  39. import Language.Haskell.Meta.Utils (conName, conTypes)
  40. -- | Derive a single instance of @Newtype@.
  41. mkNewtype :: Name -> Q [Dec]
  42. mkNewtype = mkNewTypes . (:[])
  43. -- | Derive instances of @Newtype@, specified as a list of references
  44. -- to newtypes.
  45. mkNewtypes :: [Name] -> Q [Dec]
  46. mkNewtypes = mapM (\n -> rewriteFamilies =<< mkInst <$> reify n)
  47. where
  48. mkInst (TyConI (NewtypeD a b c d _)) = mkInstFor a b c d
  49. mkInst (TyConI (DataD a b c [d] _)) = mkInstFor a b c d
  50. mkInst x
  51. = error $ show x
  52. ++ " is not a Newtype or single-field single-constructor datatype."
  53. --Construct the instance declaration
  54. -- "instance Newtype (<newtype> a ...) (<field type> a ...) where"
  55. mkInstFor context name bnds con
  56. = InstanceD context
  57. ( foldl1 AppT [ ConT ''Newtype
  58. , bndrsToType (ConT name) bnds
  59. , head $ conTypes con
  60. ] )
  61. [ FunD 'pack [Clause [] (NormalB $ ConE cn) []]
  62. , FunD 'unpack [Clause [ConP cn [VarP xn]] (NormalB $ VarE xn) []]
  63. ]
  64. where
  65. cn = conName con
  66. xn = mkName "x"
  67. -- Given a root type and a list of type variables, converts for use as
  68. -- parameters to the newtype's type in the instance head.
  69. bndrsToType :: Type -> [TyVarBndr] -> Type
  70. bndrsToType = foldl (\x y -> AppT x $ bndrToType y)
  71. -- This converts a type variable binding to a type. Preserving kind
  72. -- signatures is probably unnecessary, but we might as well.
  73. bndrToType :: TyVarBndr -> Type
  74. bndrToType (PlainTV x) = VarT x
  75. bndrToType (KindedTV x k) = SigT (VarT x) k
  76. -- This rewrites type family instances to equality constraints.
  77. rewriteFamilies :: Dec -> Q Dec
  78. rewriteFamilies (InstanceD preds ity ds) = do
  79. -- Infos of every type constructor that's applied to something else.
  80. infos <- mapM (\(n, t) -> (n, t, ) <$> reify n) $ apps ity
  81. -- Every unique family constraint found, each with a new name.
  82. fams <- mapM (\(ns, t) -> (ns, t, ) . VarT <$> newName "f")
  83. . mergeApps . catMaybes $ map justFamily infos
  84. -- Build resulting instance.
  85. return $ InstanceD (preds' fams) (ity' fams) ds
  86. where
  87. -- Selects for just family declarations, and yields the name used to
  88. -- refer to it, along with the cannonical reified name and the passed
  89. -- type.
  90. justFamily :: (Name, Type, Info) -> Maybe (Name, (Name, Type))
  91. #if __GLASGOW_HASKELL__ >= 704
  92. justFamily (n, t, FamilyI (FamilyD _ n' _ _) _) = Just (n, (n', t))
  93. #else
  94. justFamily (n, t, TyConI (FamilyD _ n' _ _)) = Just (n, (n', t))
  95. #endif
  96. justFamily _ = Nothing
  97. -- Merges all of the identical applications of the family constructor.
  98. mergeApps :: [(Name, (Name, Type))] -> [([Name], Type)]
  99. mergeApps = map (nub . map fst &&& (snd . snd . head))
  100. . groupBy ((==) `on` snd) . sortBy (comparing snd)
  101. preds' = (preds ++)
  102. . map (\((n:_), t, v) -> EqualP v (AppT (ConT n) t))
  103. ity' :: [([Name], Type, Type)] -> Type
  104. ity' fams = everywhere' (id `extT` handleType) ity
  105. where
  106. handleType :: Type -> Type
  107. handleType app@(AppT (ConT n) r)
  108. = case find (\(ns, t, _) -> n `elem` ns && t == r) fams of
  109. Just (_, _, v) -> v
  110. Nothing -> app
  111. handleType t = t
  112. -- Enumerates all of the found instances of an application of a
  113. -- type constructor.
  114. apps :: Type -> [(Name, Type)]
  115. apps = handleType
  116. where
  117. handleType :: Type -> [(Name, Type)]
  118. handleType (AppT (ConT v) r) = (v, r) : handleType r
  119. handleType (AppT (SigT t _) r) = handleType (AppT t r)
  120. --TODO: any conceivable reason to special-case (AppT (ForallT ...)) ?
  121. -- handleType (AppT (SigT t) r) =
  122. handleType t = generic t
  123. generic :: Data a => a -> [(Name, Type)]
  124. generic = concat . gmapQ (const [] `extQ` handleType)
  125. rewriteFamilies d = return d
  126. {-# DEPRECATED mkNewType "Use mkNewtype instead (capitalization)." #-}
  127. -- | Compatibility with an old, ill-capitalized name.
  128. mkNewType :: Name -> Q [Dec]
  129. mkNewType = mkNewtype
  130. {-# DEPRECATED mkNewTypes "Use mkNewtypes instead (capitalization)." #-}
  131. -- | Compatibility with an old, ill-capitalized name.
  132. mkNewTypes :: [Name] -> Q [Dec]
  133. mkNewTypes = mkNewtypes