#ifndef MIN_VERSION_template_haskell
#define MIN_VERSION_template_haskell(x,y,z) 1
#endif
module Data.Bifunctor.TH (
deriveBifunctor
, makeBimap
, deriveBifoldable
, makeBifold
, makeBifoldMap
, makeBifoldr
, makeBifoldl
, deriveBitraversable
, makeBitraverse
, makeBisequenceA
, makeBimapM
, makeBisequence
) where
import Control.Monad (guard)
import Data.Bifunctor.TH.Internal
import Data.List
import Data.Maybe
#if __GLASGOW_HASKELL__ < 710 && MIN_VERSION_template_haskell(2,8,0)
import qualified Data.Set as Set
#endif
import Language.Haskell.TH.Lib
import Language.Haskell.TH.Ppr
import Language.Haskell.TH.Syntax
deriveBifunctor :: Name -> Q [Dec]
deriveBifunctor = deriveBiClass Bifunctor
makeBimap :: Name -> Q Exp
makeBimap = makeBiFun Bimap
deriveBifoldable :: Name -> Q [Dec]
deriveBifoldable = deriveBiClass Bifoldable
makeBifold :: Name -> Q Exp
makeBifold name = appsE [ makeBifoldMap name
, varE idValName
, varE idValName
]
makeBifoldMap :: Name -> Q Exp
makeBifoldMap = makeBiFun BifoldMap
makeBifoldr :: Name -> Q Exp
makeBifoldr = makeBiFun Bifoldr
makeBifoldl :: Name -> Q Exp
makeBifoldl name = do
f <- newName "f"
g <- newName "g"
z <- newName "z"
t <- newName "t"
lamE [varP f, varP g, varP z, varP t] $
appsE [ varE appEndoValName
, appsE [ varE getDualValName
, appsE [ makeBifoldMap name, foldFun f, foldFun g, varE t]
]
, varE z
]
where
foldFun :: Name -> Q Exp
foldFun n = infixApp (conE dualDataName)
(varE composeValName)
(infixApp (conE endoDataName)
(varE composeValName)
(varE flipValName `appE` varE n)
)
deriveBitraversable :: Name -> Q [Dec]
deriveBitraversable = deriveBiClass Bitraversable
makeBitraverse :: Name -> Q Exp
makeBitraverse = makeBiFun Bitraverse
makeBisequenceA :: Name -> Q Exp
makeBisequenceA name = appsE [ makeBitraverse name
, varE idValName
, varE idValName
]
makeBimapM :: Name -> Q Exp
makeBimapM name = do
f <- newName "f"
g <- newName "g"
lamE [varP f, varP g] . infixApp (varE unwrapMonadValName) (varE composeValName) $
appsE [makeBitraverse name, wrapMonadExp f, wrapMonadExp g]
where
wrapMonadExp :: Name -> Q Exp
wrapMonadExp n = infixApp (conE wrapMonadDataName) (varE composeValName) (varE n)
makeBisequence :: Name -> Q Exp
makeBisequence name = appsE [ makeBimapM name
, varE idValName
, varE idValName
]
deriveBiClass :: BiClass -> Name -> Q [Dec]
deriveBiClass biClass tyConName = do
info <- reify tyConName
case info of
TyConI{} -> deriveBiClassPlainTy biClass tyConName
#if MIN_VERSION_template_haskell(2,7,0)
DataConI{} -> deriveBiClassDataFamInst biClass tyConName
FamilyI (FamilyD DataFam _ _ _) _ ->
error $ ns ++ "Cannot use a data family name. Use a data family instance constructor instead."
FamilyI (FamilyD TypeFam _ _ _) _ ->
error $ ns ++ "Cannot use a type family name."
_ -> error $ ns ++ "The name must be of a plain type constructor or data family instance constructor."
#else
DataConI{} -> dataConIError
_ -> error $ ns ++ "The name must be of a plain type constructor."
#endif
where
ns :: String
ns = "Data.Bifunctor.TH.deriveBiClass: "
deriveBiClassPlainTy :: BiClass -> Name -> Q [Dec]
deriveBiClassPlainTy biClass tyConName = withTyCon tyConName fromCons where
className :: Name
className = biClassName biClass
fromCons :: Cxt -> [TyVarBndr] -> [Con] -> Q [Dec]
fromCons ctxt tvbs cons = (:[]) `fmap`
instanceD (return instanceCxt)
(return $ AppT (ConT className) instanceType)
(biFunDecs biClass droppedNbs cons)
where
(instanceCxt, instanceType, droppedNbs) =
cxtAndTypePlainTy biClass tyConName ctxt tvbs
#if MIN_VERSION_template_haskell(2,7,0)
deriveBiClassDataFamInst :: BiClass -> Name -> Q [Dec]
deriveBiClassDataFamInst biClass dataFamInstName = withDataFamInstCon dataFamInstName fromDec where
className :: Name
className = biClassName biClass
fromDec :: [TyVarBndr] -> Cxt -> Name -> [Type] -> [Con] -> Q [Dec]
fromDec famTvbs ctxt parentName instTys cons = (:[]) `fmap`
instanceD (return instanceCxt)
(return $ AppT (ConT className) instanceType)
(biFunDecs biClass droppedNbs cons)
where
(instanceCxt, instanceType, droppedNbs) =
cxtAndTypeDataFamInstCon biClass parentName ctxt famTvbs instTys
#endif
biFunDecs :: BiClass -> [NameBase] -> [Con] -> [Q Dec]
biFunDecs biClass nbs cons = map makeFunD $ biClassToFuns biClass where
makeFunD :: BiFun -> Q Dec
makeFunD biFun =
funD (biFunName biFun)
[ clause []
(normalB $ makeBiFunForCons biFun nbs cons)
[]
]
makeBiFun :: BiFun -> Name -> Q Exp
makeBiFun biFun tyConName = do
info <- reify tyConName
case info of
TyConI{} -> withTyCon tyConName $ \ctxt tvbs decs ->
let !nbs = thd3 $ cxtAndTypePlainTy (biFunToClass biFun) tyConName ctxt tvbs
in makeBiFunForCons biFun nbs decs
#if MIN_VERSION_template_haskell(2,7,0)
DataConI{} -> withDataFamInstCon tyConName $ \famTvbs ctxt parentName instTys cons ->
let !nbs = thd3 $ cxtAndTypeDataFamInstCon (biFunToClass biFun) parentName ctxt famTvbs instTys
in makeBiFunForCons biFun nbs cons
FamilyI (FamilyD DataFam _ _ _) _ ->
error $ ns ++ "Cannot use a data family name. Use a data family instance constructor instead."
FamilyI (FamilyD TypeFam _ _ _) _ ->
error $ ns ++ "Cannot use a type family name."
_ -> error $ ns ++ "The name must be of a plain type constructor or data family instance constructor."
#else
DataConI{} -> dataConIError
_ -> error $ ns ++ "The name must be of a plain type constructor."
#endif
where
ns :: String
ns = "Data.Bifunctor.TH.makeBiFun: "
makeBiFunForCons :: BiFun -> [NameBase] -> [Con] -> Q Exp
makeBiFunForCons biFun nbs cons = do
argNames <- mapM newName $ catMaybes [ Just "f"
, Just "g"
, guard (biFun == Bifoldr) >> Just "z"
, Just "value"
]
let (maps,others) = splitAt 2 argNames
z = head others
value = last others
tvis = zip nbs maps
lamE (map varP argNames)
. appsE
$ [ varE $ biFunConstName biFun
, if null cons
then appE (varE errorValName)
(stringE $ "Void " ++ nameBase (biFunName biFun))
else caseE (varE value)
(map (makeBiFunForCon biFun z tvis) cons)
] ++ map varE argNames
makeBiFunForCon :: BiFun -> Name -> [TyVarInfo] -> Con -> Q Match
makeBiFunForCon biFun z tvis (NormalC conName tys) = do
args <- newNameList "arg" $ length tys
let argTys = map snd tys
makeBiFunForArgs biFun z tvis conName argTys args
makeBiFunForCon biFun z tvis (RecC conName tys) = do
args <- newNameList "arg" $ length tys
let argTys = map thd3 tys
makeBiFunForArgs biFun z tvis conName argTys args
makeBiFunForCon biFun z tvis (InfixC (_, argTyL) conName (_, argTyR)) = do
argL <- newName "argL"
argR <- newName "argR"
makeBiFunForArgs biFun z tvis conName [argTyL, argTyR] [argL, argR]
makeBiFunForCon biFun z tvis (ForallC tvbs faCxt con)
| any (`predMentionsNameBase` map fst tvis) faCxt && not (allowExQuant (biFunToClass biFun))
= existentialContextError (constructorName con)
| otherwise = makeBiFunForCon biFun z (removeForalled tvbs tvis) con
makeBiFunForArgs :: BiFun
-> Name
-> [TyVarInfo]
-> Name
-> [Type]
-> [Name]
-> Q Match
makeBiFunForArgs biFun z tvis conName tys args =
match (conP conName $ map varP args)
(normalB $ biFunCombine biFun conName z mappedArgs)
[]
where
mappedArgs :: [Q Exp]
mappedArgs = zipWith (makeBiFunForArg biFun tvis conName) tys args
makeBiFunForArg :: BiFun
-> [TyVarInfo]
-> Name
-> Type
-> Name
-> Q Exp
makeBiFunForArg biFun tvis conName ty tyExpName = do
ty' <- expandSyn ty
makeBiFunForArg' biFun tvis conName ty' tyExpName
makeBiFunForArg' :: BiFun
-> [TyVarInfo]
-> Name
-> Type
-> Name
-> Q Exp
makeBiFunForArg' biFun tvis conName ty tyExpName =
makeBiFunForType biFun tvis conName True ty `appE` varE tyExpName
makeBiFunForType :: BiFun
-> [TyVarInfo]
-> Name
-> Bool
-> Type
-> Q Exp
makeBiFunForType biFun tvis conName covariant (VarT tyName) =
case lookup (NameBase tyName) tvis of
Just mapName -> varE $ if covariant
then mapName
else contravarianceError conName
Nothing -> biFunTriv biFun
makeBiFunForType biFun tvis conName covariant (SigT ty _) =
makeBiFunForType biFun tvis conName covariant ty
makeBiFunForType biFun tvis conName covariant (ForallT tvbs _ ty) =
makeBiFunForType biFun (removeForalled tvbs tvis) conName covariant ty
makeBiFunForType biFun tvis conName covariant ty =
let tyCon :: Type
tyArgs :: [Type]
tyCon:tyArgs = unapplyTy ty
numLastArgs :: Int
numLastArgs = min 2 $ length tyArgs
lhsArgs, rhsArgs :: [Type]
(lhsArgs, rhsArgs) = splitAt (length tyArgs numLastArgs) tyArgs
tyVarNameBases :: [NameBase]
tyVarNameBases = map fst tvis
mentionsTyArgs :: Bool
mentionsTyArgs = any (`mentionsNameBase` tyVarNameBases) tyArgs
makeBiFunTuple :: Type -> Name -> Q Exp
makeBiFunTuple fieldTy fieldName =
makeBiFunForType biFun tvis conName covariant fieldTy `appE` varE fieldName
in case tyCon of
ArrowT
| not (allowFunTys (biFunToClass biFun)) -> noFunctionsError conName
| mentionsTyArgs, [argTy, resTy] <- tyArgs ->
do x <- newName "x"
b <- newName "b"
lamE [varP x, varP b] $
covBiFun covariant resTy `appE` (varE x `appE`
(covBiFun (not covariant) argTy `appE` varE b))
where
covBiFun :: Bool -> Type -> Q Exp
covBiFun = makeBiFunForType biFun tvis conName
TupleT n
| n > 0 && mentionsTyArgs -> do
args <- mapM newName $ catMaybes [ Just "x"
, guard (biFun == Bifoldr) >> Just "z"
]
xs <- newNameList "tup" n
let x = head args
z = last args
lamE (map varP args) $ caseE (varE x)
[ match (tupP $ map varP xs)
(normalB $ biFunCombine biFun
(tupleDataName n)
z
(zipWith makeBiFunTuple tyArgs xs)
)
[]
]
_ -> do
itf <- isTyFamily tyCon
if any (`mentionsNameBase` tyVarNameBases) lhsArgs || (itf && mentionsTyArgs)
then outOfPlaceTyVarError conName tyVarNameBases
else if any (`mentionsNameBase` tyVarNameBases) rhsArgs
then biFunApp biFun . appsE $
( varE (fromJust $ biFunArity biFun numLastArgs)
: map (makeBiFunForType biFun tvis conName covariant) rhsArgs
)
else biFunTriv biFun
withTyCon :: Name
-> (Cxt -> [TyVarBndr] -> [Con] -> Q a)
-> Q a
withTyCon name f = do
info <- reify name
case info of
TyConI dec ->
case dec of
DataD ctxt _ tvbs cons _ -> f ctxt tvbs cons
NewtypeD ctxt _ tvbs con _ -> f ctxt tvbs [con]
_ -> error $ ns ++ "Unsupported type " ++ show dec ++ ". Must be a data type or newtype."
_ -> error $ ns ++ "The name must be of a plain type constructor."
where
ns :: String
ns = "Data.Bifunctor.TH.withTyCon: "
#if MIN_VERSION_template_haskell(2,7,0)
withDataFam :: Name
-> ([TyVarBndr] -> [Dec] -> Q a)
-> Q a
withDataFam name f = do
info <- reify name
case info of
FamilyI (FamilyD DataFam _ tvbs _) decs -> f tvbs decs
FamilyI (FamilyD TypeFam _ _ _) _ -> error $ ns ++ "Cannot use a type family name."
_ -> error $ ns ++ "Unsupported type " ++ show info ++ ". Must be a data family name."
where
ns :: String
ns = "Data.Bifunctor.TH.withDataFam: "
withDataFamInstCon :: Name
-> ([TyVarBndr] -> Cxt -> Name -> [Type] -> [Con] -> Q a)
-> Q a
withDataFamInstCon dficName f = do
dficInfo <- reify dficName
case dficInfo of
DataConI _ _ parentName _ -> do
parentInfo <- reify parentName
case parentInfo of
FamilyI (FamilyD DataFam _ _ _) _ -> withDataFam parentName $ \famTvbs decs ->
let sameDefDec = flip find decs $ \dec ->
case dec of
DataInstD _ _ _ cons' _ -> any ((dficName ==) . constructorName) cons'
NewtypeInstD _ _ _ con _ -> dficName == constructorName con
_ -> error $ ns ++ "Must be a data or newtype instance."
(ctxt, instTys, cons) = case sameDefDec of
Just (DataInstD ctxt' _ instTys' cons' _) -> (ctxt', instTys', cons')
Just (NewtypeInstD ctxt' _ instTys' con _) -> (ctxt', instTys', [con])
_ -> error $ ns ++ "Could not find data or newtype instance constructor."
in f famTvbs ctxt parentName instTys cons
_ -> error $ ns ++ "Data constructor " ++ show dficName ++ " is not from a data family instance."
_ -> error $ ns ++ "Unsupported type " ++ show dficInfo ++ ". Must be a data family instance constructor."
where
ns :: String
ns = "Data.Bifunctor.TH.withDataFamInstCon: "
#endif
cxtAndTypePlainTy :: BiClass
-> Name
-> Cxt
-> [TyVarBndr]
-> (Cxt, Type, [NameBase])
cxtAndTypePlainTy biClass tyConName dataCxt tvbs
| remainingLength < 0 || not (wellKinded droppedKinds)
= derivingKindError biClass tyConName
| any (`predMentionsNameBase` droppedNbs) dataCxt
= datatypeContextError tyConName instanceType
| otherwise = (instanceCxt, instanceType, droppedNbs)
where
instanceCxt :: Cxt
instanceCxt = mapMaybe (applyConstraint biClass) remaining
instanceType :: Type
instanceType = applyTyCon tyConName $ map (VarT . tvbName) remaining
remainingLength :: Int
remainingLength = length tvbs 2
remaining, dropped :: [TyVarBndr]
(remaining, dropped) = splitAt remainingLength tvbs
droppedKinds :: [Kind]
droppedKinds = map tvbKind dropped
droppedNbs :: [NameBase]
droppedNbs = map (NameBase . tvbName) dropped
#if MIN_VERSION_template_haskell(2,7,0)
cxtAndTypeDataFamInstCon :: BiClass
-> Name
-> Cxt
-> [TyVarBndr]
-> [Type]
-> (Cxt, Type, [NameBase])
cxtAndTypeDataFamInstCon biClass parentName dataCxt famTvbs instTysAndKinds
| remainingLength < 0 || not (wellKinded droppedKinds)
= derivingKindError biClass parentName
| any (`predMentionsNameBase` droppedNbs) dataCxt
= datatypeContextError parentName instanceType
| canEtaReduce remaining dropped
= (instanceCxt, instanceType, droppedNbs)
| otherwise = etaReductionError instanceType
where
instanceCxt :: Cxt
instanceCxt = mapMaybe (applyConstraint biClass) lhsTvbs
instanceType :: Type
instanceType = applyTyCon parentName
$ map unSigT remaining
remainingLength :: Int
remainingLength = length famTvbs 2
remaining, dropped :: [Type]
(remaining, dropped) = splitAt remainingLength rhsTypes
droppedKinds :: [Kind]
droppedKinds = map tvbKind . snd $ splitAt remainingLength famTvbs
droppedNbs :: [NameBase]
droppedNbs = map varTToNameBase dropped
instTypes :: [Type]
instTypes =
# if __GLASGOW_HASKELL__ >= 710 || !(MIN_VERSION_template_haskell(2,8,0))
instTysAndKinds
# else
drop (Set.size . Set.unions $ map (distinctKindVars . tvbKind) famTvbs)
instTysAndKinds
# endif
lhsTvbs :: [TyVarBndr]
lhsTvbs = map (uncurry replaceTyVarName)
. filter (isTyVar . snd)
. take remainingLength
$ zip famTvbs rhsTypes
rhsTypes :: [Type]
rhsTypes =
# if __GLASGOW_HASKELL__ >= 708 && __GLASGOW_HASKELL__ < 710
instTypes ++ map tvbToType (drop (length instTypes) famTvbs)
# else
instTypes
# endif
#endif
applyConstraint :: BiClass -> TyVarBndr -> Maybe Pred
applyConstraint _ (PlainTV _) = Nothing
applyConstraint biClass (KindedTV name kind) = do
constraint <- biClassConstraint biClass $ numKindArrows kind
if canRealizeKindStarChain kind
then Just $ applyClass constraint name
else Nothing
derivingKindError :: BiClass -> Name -> a
derivingKindError biClass tyConName = error
. showString "Cannot derive well-kinded instance of form ‘"
. showString className
. showChar ' '
. showParen True
( showString (nameBase tyConName)
. showString " ..."
)
. showString "‘\n\tClass "
. showString className
. showString " expects an argument of kind * -> * -> *"
$ ""
where
className :: String
className = nameBase $ biClassName biClass
contravarianceError :: Name -> a
contravarianceError conName = error
. showString "Constructor ‘"
. showString (nameBase conName)
. showString "‘ must not use the last type variable(s) in a function argument"
$ ""
noFunctionsError :: Name -> a
noFunctionsError conName = error
. showString "Constructor ‘"
. showString (nameBase conName)
. showString "‘ must not contain function types"
$ ""
datatypeContextError :: Name -> Type -> a
datatypeContextError dataName instanceType = error
. showString "Can't make a derived instance of ‘"
. showString (pprint instanceType)
. showString "‘:\n\tData type ‘"
. showString (nameBase dataName)
. showString "‘ must not have a class context involving the last type argument(s)"
$ ""
existentialContextError :: Name -> a
existentialContextError conName = error
. showString "Constructor ‘"
. showString (nameBase conName)
. showString "‘ must be truly polymorphic in the last argument(s) of the data type"
$ ""
outOfPlaceTyVarError :: Name -> [NameBase] -> a
outOfPlaceTyVarError conName tyVarNames = error
. showString "Constructor ‘"
. showString (nameBase conName)
. showString "‘ must use the type variable(s) "
. shows tyVarNames
. showString " only in the last argument(s) of a data type"
$ ""
#if MIN_VERSION_template_haskell(2,7,0)
etaReductionError :: Type -> a
etaReductionError instanceType = error $
"Cannot eta-reduce to an instance of form \n\tinstance (...) => "
++ pprint instanceType
#else
dataConIError :: a
dataConIError = error
. showString "Cannot use a data constructor."
. showString "\n\t(Note: if you are trying to derive for a data family instance,"
. showString "\n\tuse GHC >= 7.4 instead.)"
$ ""
#endif
data BiClass = Bifunctor | Bifoldable | Bitraversable
data BiFun = Bimap | Bifoldr | BifoldMap | Bitraverse
deriving Eq
biFunConstName :: BiFun -> Name
biFunConstName Bimap = bimapConstValName
biFunConstName Bifoldr = bifoldrConstValName
biFunConstName BifoldMap = bifoldMapConstValName
biFunConstName Bitraverse = bitraverseConstValName
biClassName :: BiClass -> Name
biClassName Bifunctor = bifunctorTypeName
biClassName Bifoldable = bifoldableTypeName
biClassName Bitraversable = bitraversableTypeName
biFunName :: BiFun -> Name
biFunName Bimap = bimapValName
biFunName Bifoldr = bifoldrValName
biFunName BifoldMap = bifoldMapValName
biFunName Bitraverse = bitraverseValName
biClassToFuns :: BiClass -> [BiFun]
biClassToFuns Bifunctor = [Bimap]
biClassToFuns Bifoldable = [Bifoldr, BifoldMap]
biClassToFuns Bitraversable = [Bitraverse]
biFunToClass :: BiFun -> BiClass
biFunToClass Bimap = Bifunctor
biFunToClass Bifoldr = Bifoldable
biFunToClass BifoldMap = Bifoldable
biFunToClass Bitraverse = Bitraversable
biClassConstraint :: BiClass -> Int -> Maybe Name
biClassConstraint Bifunctor 1 = Just functorTypeName
biClassConstraint Bifoldable 1 = Just foldableTypeName
biClassConstraint Bitraversable 1 = Just traversableTypeName
biClassConstraint biClass 2 = Just $ biClassName biClass
biClassConstraint _ _ = Nothing
biFunArity :: BiFun -> Int -> Maybe Name
biFunArity Bimap 1 = Just fmapValName
biFunArity Bifoldr 1 = Just foldrValName
biFunArity BifoldMap 1 = Just foldMapValName
biFunArity Bitraverse 1 = Just traverseValName
biFunArity biFun 2 = Just $ biFunName biFun
biFunArity _ _ = Nothing
allowFunTys :: BiClass -> Bool
allowFunTys Bifunctor = True
allowFunTys _ = False
allowExQuant :: BiClass -> Bool
allowExQuant Bifoldable = True
allowExQuant _ = False
biFunTriv :: BiFun -> Q Exp
biFunTriv Bimap = do
x <- newName "x"
lamE [varP x] $ varE x
biFunTriv Bifoldr = do
z <- newName "z"
lamE [wildP, varP z] $ varE z
biFunTriv BifoldMap = lamE [wildP] $ varE memptyValName
biFunTriv Bitraverse = varE pureValName
biFunApp :: BiFun -> Q Exp -> Q Exp
biFunApp Bifoldr e = do
x <- newName "x"
z <- newName "z"
lamE [varP x, varP z] $ appsE [e, varE z, varE x]
biFunApp _ e = e
biFunCombine :: BiFun -> Name -> Name -> [Q Exp] -> Q Exp
biFunCombine Bimap = bimapCombine
biFunCombine Bifoldr = bifoldrCombine
biFunCombine BifoldMap = bifoldMapCombine
biFunCombine Bitraverse = bitraverseCombine
bimapCombine :: Name -> Name -> [Q Exp] -> Q Exp
bimapCombine conName _ = foldl' appE (conE conName)
bifoldrCombine :: Name -> Name -> [Q Exp] -> Q Exp
bifoldrCombine _ zName = foldr appE (varE zName)
bifoldMapCombine :: Name -> Name -> [Q Exp] -> Q Exp
bifoldMapCombine _ _ [] = varE memptyValName
bifoldMapCombine _ _ es = foldr1 (appE . appE (varE mappendValName)) es
bitraverseCombine :: Name -> Name -> [Q Exp] -> Q Exp
bitraverseCombine conName _ [] = varE pureValName `appE` conE conName
bitraverseCombine conName _ (e:es) =
foldl' (flip infixApp $ varE apValName)
(appsE [varE fmapValName, conE conName, e]) es