module Data.Label.Derive
(
mkLabel
, mkLabels
, mkLabelsNamed
, getLabel
, fclabels
, mkLabelsWith
, getLabelWith
, defaultNaming
)
where
import Control.Applicative
import Control.Arrow
import Control.Category
import Control.Monad
import Data.Char (toLower, toUpper)
import Data.Foldable (Foldable, toList)
import Data.Label.Point
import Data.List (groupBy, sortBy, delete, nub)
import Data.Maybe (fromMaybe)
import Data.Ord
import Data.String
import Language.Haskell.TH
import Prelude hiding ((.), id)
import qualified Data.Label.Mono as Mono
import qualified Data.Label.Poly as Poly
mkLabels :: [Name] -> Q [Dec]
mkLabels = liftM concat . mapM (mkLabelsWith defaultNaming True False False True)
mkLabel :: Name -> Q [Dec]
mkLabel = mkLabels . return
mkLabelsNamed :: (String -> String) -> [Name] -> Q [Dec]
mkLabelsNamed mk = liftM concat . mapM (mkLabelsWith mk True False False True)
getLabel :: Name -> Q Exp
getLabel = getLabelWith True False False
getLabelWith
:: Bool
-> Bool
-> Bool
-> Name
-> Q Exp
getLabelWith sigs concrete failing name =
do dec <- reifyDec name
labels <- generateLabels id concrete failing dec
let bodies = map (\(LabelExpr _ _ _ b) -> b) labels
types = map (\(LabelExpr _ _ t _) -> t) labels
context = head $ map (\(LabelExpr _ c _ _) -> c) labels
vars = head $ map (\(LabelExpr v _ _ _) -> v) labels
if sigs
then tupE bodies `sigE`
forallT vars context (foldl appT (tupleT (length bodies)) types)
else tupE bodies
mkLabelsWith
:: (String -> String)
-> Bool
-> Bool
-> Bool
-> Bool
-> Name
-> Q [Dec]
mkLabelsWith mk sigs concrete failing inl name =
do dec <- reifyDec name
mkLabelsWithForDec mk sigs concrete failing inl dec
defaultNaming :: String -> String
defaultNaming field =
case field of
'_' : c : rest -> toLower c : rest
f : rest -> 'l' : toUpper f : rest
n -> fclError ("Cannot derive label for record selector with name: " ++ n)
fclabels :: Q [Dec] -> Q [Dec]
fclabels decls =
do ds <- decls
ls <- forM (ds >>= labels) (mkLabelsWithForDec id True False False False)
return (concat ((delabelize <$> ds) : ls))
where
labels :: Dec -> [Dec]
labels dec =
case dec of
DataD {} -> [dec]
NewtypeD {} -> [dec]
_ -> []
delabelize :: Dec -> Dec
delabelize dec =
case dec of
DataD ctx nm vars cs ns -> DataD ctx nm vars (con <$> cs) ns
NewtypeD ctx nm vars c ns -> NewtypeD ctx nm vars (con c) ns
rest -> rest
where con (RecC n vst) = NormalC n (map (\(_, s, t) -> (s, t)) vst)
con c = c
data Label
= LabelDecl
Name
DecQ
[TyVarBndr]
CxtQ
TypeQ
ExpQ
| LabelExpr
[TyVarBndr]
CxtQ
TypeQ
ExpQ
data Field c = Field
(Maybe Name)
Bool
Type
c
deriving (Eq, Functor, Foldable)
type Subst = [(Type, Type)]
data Context = Context
Int
Name
Con
deriving (Eq, Show)
data Typing = Typing
Bool
TypeQ
TypeQ
[TyVarBndr]
mkLabelsWithForDec :: (String -> String) -> Bool -> Bool -> Bool -> Bool -> Dec -> Q [Dec]
mkLabelsWithForDec mk sigs concrete failing inl dec =
do labels <- generateLabels mk concrete failing dec
decls <- forM labels $ \l ->
case l of
LabelExpr {} -> return []
LabelDecl n i v c t b ->
do bdy <- pure <$> funD n [clause [] (normalB b) []]
prg <- if inl then pure <$> i else return []
typ <- if sigs
then pure <$> sigD n (forallT v c t)
else return []
return (concat [prg, typ, bdy])
return (concat decls)
generateLabels :: (String -> String) -> Bool -> Bool -> Dec -> Q [Label]
generateLabels mk concrete failing dec =
do
let (name, cons, vars) =
case dec of
DataD _ n vs cs _ -> (n, cs, vs)
NewtypeD _ n vs c _ -> (n, [c], vs)
_ -> fclError "Can only derive labels for datatypes and newtypes."
fields = groupFields mk cons
forM fields $ generateLabel failing concrete name vars cons
groupFields :: (String -> String) -> [Con] -> [Field ([Context], Subst)]
groupFields mk
= map (rename mk)
. concatMap (\fs -> let vals = concat (toList <$> fs)
cons = fst <$> vals
subst = concat (snd <$> vals)
in nub (fmap (const (cons, subst)) <$> fs)
)
. groupBy eq
. sortBy (comparing name)
. concatMap constructorFields
where name (Field n _ _ _) = n
eq f g = False `fromMaybe` ((==) <$> name f <*> name g)
rename f (Field n a b c) =
Field (mkName . f . nameBase <$> n) a b c
constructorFields :: Con -> [Field (Context, Subst)]
constructorFields con =
case con of
NormalC c fs -> one <$> zip [0..] fs
where one (i, f@(_, ty)) = Field Nothing mono ty (Context i c con, [])
where fsTys = map (typeVariables . snd) (delete f fs)
mono = any (\x -> any (elem x) fsTys) (typeVariables ty)
RecC c fs -> one <$> zip [0..] fs
where one (i, f@(n, _, ty)) = Field (Just n) mono ty (Context i c con, [])
where fsTys = map (typeVariables . trd) (delete f fs)
mono = any (\x -> any (elem x) fsTys) (typeVariables ty)
trd (_, _, x) = x
InfixC a c b -> one <$> [(0, a), (1, b)]
where one (i, (_, ty)) = Field Nothing mono ty (Context i c con, [])
where fsTys = map (typeVariables . snd) [a, b]
mono = any (\x -> any (elem x) fsTys) (typeVariables ty)
ForallC x y v -> setEqs <$> constructorFields v
where eqs = [ (a, b) | EqualP a b <- y ]
setEqs (Field a b c d) = Field a b c (first upd . second (eqs ++) $ d)
upd (Context a b c) = Context a b (ForallC x y c)
prune :: [Context] -> [Con] -> [Con]
prune contexts allCons =
case contexts of
(Context _ _ con) : _
-> filter (unifiableCon con) allCons
[] -> []
unifiableCon :: Con -> Con -> Bool
unifiableCon a b = and (zipWith unifiable (indices a) (indices b))
where indices con =
case con of
NormalC {} -> []
RecC {} -> []
InfixC {} -> []
ForallC _ x _ -> [ c | EqualP _ c <- x ]
unifiable :: Type -> Type -> Bool
unifiable x y =
case (x, y) of
( VarT _ , _ ) -> True
( _ , VarT _ ) -> True
( AppT a b , AppT c d ) -> unifiable a c && unifiable b d
( SigT t k , SigT s j ) -> unifiable t s && k == j
( ForallT _ _ t , ForallT _ _ s ) -> unifiable t s
( a , b ) -> a == b
generateLabel
:: Bool
-> Bool
-> Name
-> [TyVarBndr]
-> [Con]
-> Field ([Context], Subst)
-> Q Label
generateLabel failing concrete datatype dtVars allCons
field@(Field name forcedMono fieldtype (contexts, subst)) =
do let total = length contexts == length (prune contexts allCons)
(Typing mono tyI tyO vars)
<- computeTypes forcedMono fieldtype datatype dtVars subst
let cat = varT (mkName "cat")
failE = if failing
then [| failArrow |]
else [| zeroArrow |]
getT = [| arr $(getter total field) |]
putT = [| arr $(setter total field) |]
getP = [| $(failE) ||| id <<< $getT |]
putP = [| $(failE) ||| id <<< $putT |]
failP = if failing
then classP ''ArrowFail [ [t| String |], cat]
else classP ''ArrowZero [cat]
ctx = if total
then cxt [ classP ''ArrowApply [cat] ]
else cxt [ classP ''ArrowChoice [cat]
, classP ''ArrowApply [cat]
, failP
]
body = if total
then [| Poly.point $ Point $getT (modifier $getT $putT) |]
else [| Poly.point $ Point $getP (modifier $getP $putP) |]
cont = if concrete
then cxt []
else ctx
partial = if failing
then [t| Failing String |]
else [t| Partial |]
concTy = if total
then if mono
then [t| Mono.Lens Total $tyI $tyO |]
else [t| Poly.Lens Total $tyI $tyO |]
else if mono
then [t| Mono.Lens $partial $tyI $tyO |]
else [t| Poly.Lens $partial $tyI $tyO |]
ty = if concrete
then concTy
else if mono
then [t| Mono.Lens $cat $tyI $tyO |]
else [t| Poly.Lens $cat $tyI $tyO |]
tvs <- nub . binderFromType <$> ty
return $
case name of
Nothing -> LabelExpr tvs cont ty body
Just n ->
#if MIN_VERSION_template_haskell(2,8,0)
let inline = InlineP n Inline FunLike (FromPhase 0)
#else
let inline = InlineP n (InlineSpec True True (Just (True, 0)))
#endif
in LabelDecl n (return (PragmaD inline)) tvs cont ty body
modifier :: ArrowApply cat => cat f o -> cat (i, f) g -> cat (cat o i, f) g
modifier g m = m . first app . arr (\(n, (f, o)) -> ((n, o), f)) . second (id &&& g)
getter :: Bool -> Field ([Context], Subst) -> Q Exp
getter total (Field mn _ _ (cons, _)) =
do let pt = mkName "f"
nm = maybe (tupE []) (litE . StringL . nameBase) mn
wild = if total then [] else [match wildP (normalB [| Left $(nm) |]) []]
rght = if total then id else appE [| Right |]
mkCase (Context i _ c) = match pat (normalB (rght var)) []
where (pat, var) = case1 i c
lamE [varP pt]
(caseE (varE pt) (map mkCase cons ++ wild))
where
case1 i con =
case con of
NormalC c fs -> let s = take (length fs) in (conP c (s pats), var)
RecC c fs -> let s = take (length fs) in (conP c (s pats), var)
InfixC _ c _ -> (infixP (pats !! 0) c (pats !! 1), var)
ForallC _ _ c -> case1 i c
where fresh = mkName . pure <$> delete 'f' ['a'..'z']
pats1 = varP <$> fresh
pats = replicate i wildP ++ [pats1 !! i] ++ repeat wildP
var = varE (fresh !! i)
setter :: Bool -> Field ([Context], Subst) -> Q Exp
setter total (Field mn _ _ (cons, _)) =
do let pt = mkName "f"
md = mkName "v"
nm = maybe (tupE []) (litE . StringL . nameBase) mn
wild = if total then [] else [match wildP (normalB [| Left $(nm) |]) []]
rght = if total then id else appE [| Right |]
mkCase (Context i _ c) = match pat (normalB (rght var)) []
where (pat, var) = case1 i c
lamE [tupP [varP md, varP pt]]
(caseE (varE pt) (map mkCase cons ++ wild))
where
case1 i con =
case con of
NormalC c fs -> let s = take (length fs) in (conP c (s pats), apps (conE c) (s vars))
RecC c fs -> let s = take (length fs) in (conP c (s pats), apps (conE c) (s vars))
InfixC _ c _ -> ( infixP (pats !! 0) c (pats !! 1)
, infixE (Just (vars !! 0)) (conE c) (Just (vars !! 1))
)
ForallC _ _ c -> case1 i c
where fresh = mkName . pure <$> delete 'f' (delete 'v' ['a'..'z'])
pats1 = varP <$> fresh
pats = take i pats1 ++ [wildP] ++ drop (i + 1) pats1
vars1 = varE <$> fresh
v = varE (mkName "v")
vars = take i vars1 ++ [v] ++ drop (i + 1) vars1
apps f as = foldl appE f as
computeTypes :: Bool -> Type -> Name -> [TyVarBndr] -> Subst -> Q Typing
computeTypes forcedMono fieldtype datatype dtVars_ subst =
do let fieldVars = typeVariables fieldtype
tyO = return fieldtype
dtTypes = substitute subst . typeFromBinder <$> dtVars_
dtBinders = concatMap binderFromType dtTypes
varNames = nameFromBinder <$> dtBinders
usedVars = filter (`elem` fieldVars) varNames
tyI = return $ foldr (flip AppT) (ConT datatype) (reverse dtTypes)
pretties = mapTyVarBndr pretty <$> dtBinders
mono = forcedMono || isMonomorphic fieldtype dtBinders
if mono
then return $ Typing
mono
(prettyType <$> tyI)
(prettyType <$> tyO)
(nub pretties)
else
do let names = return <$> ['a'..'z']
used = show . pretty <$> varNames
free = filter (not . (`elem` used)) names
subs <- forM (zip usedVars free) (\(a, b) -> (,) a <$> newName b)
let rename = mapTypeVariables (\a -> a `fromMaybe` lookup a subs)
return $ Typing
mono
(prettyType <$> [t| $tyI -> $(rename <$> tyI) |])
(prettyType <$> [t| $tyO -> $(rename <$> tyO) |])
(nub (pretties ++ map (mapTyVarBndr pretty) (PlainTV . snd <$> subs)))
isMonomorphic :: Type -> [TyVarBndr] -> Bool
isMonomorphic field vars =
let fieldVars = typeVariables field
varNames = nameFromBinder <$> vars
usedVars = filter (`elem` fieldVars) varNames
in null usedVars
typeVariables :: Type -> [Name]
typeVariables = map nameFromBinder . binderFromType
typeFromBinder :: TyVarBndr -> Type
typeFromBinder (PlainTV tv ) = VarT tv
typeFromBinder (KindedTV tv kind) = SigT (VarT tv) kind
binderFromType :: Type -> [TyVarBndr]
binderFromType = go
where
go ty =
case ty of
ForallT ts _ _ -> ts
AppT a b -> go a ++ go b
SigT t _ -> go t
VarT n -> [PlainTV n]
_ -> []
mapTypeVariables :: (Name -> Name) -> Type -> Type
mapTypeVariables f = go
where
go ty =
case ty of
ForallT ts a b -> ForallT (mapTyVarBndr f <$> ts)
(mapPred f <$> a) (go b)
AppT a b -> AppT (go a) (go b)
SigT t a -> SigT (go t) a
VarT n -> VarT (f n)
t -> t
mapType :: (Type -> Type) -> Type -> Type
mapType f = go
where
go ty =
case ty of
ForallT v c t -> f (ForallT v c (go t))
AppT a b -> f (AppT (go a) (go b))
SigT t k -> f (SigT (go t) k)
_ -> f ty
substitute :: Subst -> Type -> Type
substitute env = mapType sub
where sub v = case lookup v env of
Nothing -> v
Just w -> w
nameFromBinder :: TyVarBndr -> Name
nameFromBinder (PlainTV n ) = n
nameFromBinder (KindedTV n _) = n
mapPred :: (Name -> Name) -> Pred -> Pred
mapPred f (ClassP n ts) = ClassP (f n) (mapTypeVariables f <$> ts)
mapPred f (EqualP t x ) = EqualP (mapTypeVariables f t) (mapTypeVariables f x)
mapTyVarBndr :: (Name -> Name) -> TyVarBndr -> TyVarBndr
mapTyVarBndr f (PlainTV n ) = PlainTV (f n)
mapTyVarBndr f (KindedTV n a) = KindedTV (f n) a
pretty :: Name -> Name
pretty tv = mkName (takeWhile (/= '_') (show tv))
prettyType :: Type -> Type
prettyType = mapTypeVariables pretty
reifyDec :: Name -> Q Dec
reifyDec name =
do info <- reify name
case info of
TyConI dec -> return dec
_ -> fclError "Info must be type declaration type."
fclError :: String -> a
fclError err = error ("Data.Label.Derive: " ++ err)