{-# LANGUAGE CPP #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE ScopedTypeVariables #-}
#if __GLASGOW_HASKELL__ >= 704
{-# LANGUAGE Unsafe #-}
#endif
#ifndef MIN_VERSION_template_haskell
#define MIN_VERSION_template_haskell(x,y,z) 1
#endif
module Data.Bifunctor.TH (
deriveBifunctor
, deriveBifunctorOptions
, makeBimap
, makeBimapOptions
, deriveBifoldable
, deriveBifoldableOptions
, makeBifold
, makeBifoldOptions
, makeBifoldMap
, makeBifoldMapOptions
, makeBifoldr
, makeBifoldrOptions
, makeBifoldl
, makeBifoldlOptions
, deriveBitraversable
, deriveBitraversableOptions
, makeBitraverse
, makeBitraverseOptions
, makeBisequenceA
, makeBisequenceAOptions
, makeBimapM
, makeBimapMOptions
, makeBisequence
, makeBisequenceOptions
, Options(..)
, defaultOptions
) where
import Control.Monad (guard, unless, when)
import Data.Bifunctor.TH.Internal
import Data.List
import qualified Data.Map as Map ((!), fromList, keys, lookup, member, size)
import Data.Maybe
import Language.Haskell.TH.Datatype
import Language.Haskell.TH.Datatype.TyVarBndr
import Language.Haskell.TH.Lib
import Language.Haskell.TH.Ppr
import Language.Haskell.TH.Syntax
newtype Options = Options
{ emptyCaseBehavior :: Bool
} deriving (Eq, Ord, Read, Show)
defaultOptions :: Options
defaultOptions = Options { emptyCaseBehavior = False }
deriveBifunctor :: Name -> Q [Dec]
deriveBifunctor = deriveBifunctorOptions defaultOptions
deriveBifunctorOptions :: Options -> Name -> Q [Dec]
deriveBifunctorOptions = deriveBiClass Bifunctor
makeBimap :: Name -> Q Exp
makeBimap = makeBimapOptions defaultOptions
makeBimapOptions :: Options -> Name -> Q Exp
makeBimapOptions = makeBiFun Bimap
deriveBifoldable :: Name -> Q [Dec]
deriveBifoldable = deriveBifoldableOptions defaultOptions
deriveBifoldableOptions :: Options -> Name -> Q [Dec]
deriveBifoldableOptions = deriveBiClass Bifoldable
makeBifold :: Name -> Q Exp
makeBifold = makeBifoldOptions defaultOptions
makeBifoldOptions :: Options -> Name -> Q Exp
makeBifoldOptions opts name = appsE [ makeBifoldMapOptions opts name
, varE idValName
, varE idValName
]
makeBifoldMap :: Name -> Q Exp
makeBifoldMap = makeBifoldMapOptions defaultOptions
makeBifoldMapOptions :: Options -> Name -> Q Exp
makeBifoldMapOptions = makeBiFun BifoldMap
makeBifoldr :: Name -> Q Exp
makeBifoldr = makeBifoldrOptions defaultOptions
makeBifoldrOptions :: Options -> Name -> Q Exp
makeBifoldrOptions = makeBiFun Bifoldr
makeBifoldl :: Name -> Q Exp
makeBifoldl = makeBifoldlOptions defaultOptions
makeBifoldlOptions :: Options -> Name -> Q Exp
makeBifoldlOptions opts 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 [ makeBifoldMapOptions opts 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 = deriveBitraversableOptions defaultOptions
deriveBitraversableOptions :: Options -> Name -> Q [Dec]
deriveBitraversableOptions = deriveBiClass Bitraversable
makeBitraverse :: Name -> Q Exp
makeBitraverse = makeBitraverseOptions defaultOptions
makeBitraverseOptions :: Options -> Name -> Q Exp
makeBitraverseOptions = makeBiFun Bitraverse
makeBisequenceA :: Name -> Q Exp
makeBisequenceA = makeBisequenceAOptions defaultOptions
makeBisequenceAOptions :: Options -> Name -> Q Exp
makeBisequenceAOptions opts name = appsE [ makeBitraverseOptions opts name
, varE idValName
, varE idValName
]
makeBimapM :: Name -> Q Exp
makeBimapM = makeBimapMOptions defaultOptions
makeBimapMOptions :: Options -> Name -> Q Exp
makeBimapMOptions opts name = do
f <- newName "f"
g <- newName "g"
lamE [varP f, varP g] . infixApp (varE unwrapMonadValName) (varE composeValName) $
appsE [ makeBitraverseOptions opts name
, wrapMonadExp f
, wrapMonadExp g
]
where
wrapMonadExp :: Name -> Q Exp
wrapMonadExp n = infixApp (conE wrapMonadDataName) (varE composeValName) (varE n)
makeBisequence :: Name -> Q Exp
makeBisequence = makeBisequenceOptions defaultOptions
makeBisequenceOptions :: Options -> Name -> Q Exp
makeBisequenceOptions opts name = appsE [ makeBimapMOptions opts name
, varE idValName
, varE idValName
]
deriveBiClass :: BiClass -> Options -> Name -> Q [Dec]
deriveBiClass biClass opts name = do
info <- reifyDatatype name
case info of
DatatypeInfo { datatypeContext = ctxt
, datatypeName = parentName
, datatypeInstTypes = instTys
, datatypeVariant = variant
, datatypeCons = cons
} -> do
(instanceCxt, instanceType)
<- buildTypeInstance biClass parentName ctxt instTys variant
(:[]) `fmap` instanceD (return instanceCxt)
(return instanceType)
(biFunDecs biClass opts parentName instTys cons)
biFunDecs :: BiClass -> Options -> Name -> [Type] -> [ConstructorInfo] -> [Q Dec]
biFunDecs biClass opts parentName instTys cons =
map makeFunD $ biClassToFuns biClass
where
makeFunD :: BiFun -> Q Dec
makeFunD biFun =
funD (biFunName biFun)
[ clause []
(normalB $ makeBiFunForCons biFun opts parentName instTys cons)
[]
]
makeBiFun :: BiFun -> Options -> Name -> Q Exp
makeBiFun biFun opts name = do
info <- reifyDatatype name
case info of
DatatypeInfo { datatypeContext = ctxt
, datatypeName = parentName
, datatypeInstTypes = instTys
, datatypeVariant = variant
, datatypeCons = cons
} ->
buildTypeInstance (biFunToClass biFun) parentName ctxt instTys variant
>> makeBiFunForCons biFun opts parentName instTys cons
makeBiFunForCons :: BiFun -> Options -> Name -> [Type] -> [ConstructorInfo] -> Q Exp
makeBiFunForCons biFun opts _parentName instTys cons = do
map1 <- newName "f"
map2 <- newName "g"
z <- newName "z"
value <- newName "value"
let argNames = catMaybes [ Just map1
, Just map2
, guard (biFun == Bifoldr) >> Just z
, Just value
]
lastTyVars = map varTToName $ drop (length instTys - 2) instTys
tvMap = Map.fromList $ zip lastTyVars [map1, map2]
lamE (map varP argNames)
. appsE
$ [ varE $ biFunConstName biFun
, makeFun z value tvMap
] ++ map varE argNames
where
makeFun :: Name -> Name -> TyVarMap -> Q Exp
makeFun z value tvMap = do
#if MIN_VERSION_template_haskell(2,9,0)
roles <- reifyRoles _parentName
#endif
case () of
_
#if MIN_VERSION_template_haskell(2,9,0)
| Just (rs, PhantomR) <- unsnoc roles
, Just (_, PhantomR) <- unsnoc rs
-> biFunPhantom z value
#endif
| null cons && emptyCaseBehavior opts && ghc7'8OrLater
-> biFunEmptyCase biFun z value
| null cons
-> biFunNoCons biFun z value
| otherwise
-> caseE (varE value)
(map (makeBiFunForCon biFun z tvMap) cons)
ghc7'8OrLater :: Bool
#if __GLASGOW_HASKELL__ >= 708
ghc7'8OrLater = True
#else
ghc7'8OrLater = False
#endif
#if MIN_VERSION_template_haskell(2,9,0)
biFunPhantom :: Name -> Name -> Q Exp
biFunPhantom z value =
biFunTrivial coerce
(varE pureValName `appE` coerce)
biFun z
where
coerce :: Q Exp
coerce = varE coerceValName `appE` varE value
#endif
makeBiFunForCon :: BiFun -> Name -> TyVarMap -> ConstructorInfo -> Q Match
makeBiFunForCon biFun z tvMap
con@(ConstructorInfo { constructorName = conName
, constructorContext = ctxt }) = do
when ((any (`predMentionsName` Map.keys tvMap) ctxt
|| Map.size tvMap < 2)
&& not (allowExQuant (biFunToClass biFun))) $
existentialContextError conName
case biFun of
Bimap -> makeBimapMatch tvMap con
Bifoldr -> makeBifoldrMatch z tvMap con
BifoldMap -> makeBifoldMapMatch tvMap con
Bitraverse -> makeBitraverseMatch tvMap con
makeBimapMatch :: TyVarMap -> ConstructorInfo -> Q Match
makeBimapMatch tvMap con@(ConstructorInfo{constructorName = conName}) = do
parts <- foldDataConArgs tvMap ft_bimap con
match_for_con conName parts
where
ft_bimap :: FFoldType (Exp -> Q Exp)
ft_bimap = FT { ft_triv = return
, ft_var = \v x -> return $ VarE (tvMap Map.! v) `AppE` x
, ft_fun = \g h x -> mkSimpleLam $ \b -> do
gg <- g b
h $ x `AppE` gg
, ft_tup = mkSimpleTupleCase match_for_con
, ft_ty_app = \argGs x -> do
let inspect :: (Type, Exp -> Q Exp) -> Q Exp
inspect (argTy, g)
| Just argVar <- varTToName_maybe argTy
, Just f <- Map.lookup argVar tvMap
= return $ VarE f
| otherwise
= mkSimpleLam g
appsE $ varE (fmapArity (length argGs))
: map inspect argGs
++ [return x]
, ft_forall = \_ g x -> g x
, ft_bad_app = \_ -> outOfPlaceTyVarError conName
, ft_co_var = \_ _ -> contravarianceError conName
}
match_for_con :: Name -> [Exp -> Q Exp] -> Q Match
match_for_con = mkSimpleConMatch $ \conName' xs ->
appsE (conE conName':xs)
makeBifoldrMatch :: Name -> TyVarMap -> ConstructorInfo -> Q Match
makeBifoldrMatch z tvMap con@(ConstructorInfo{constructorName = conName}) = do
parts <- foldDataConArgs tvMap ft_bifoldr con
parts' <- sequence parts
match_for_con (VarE z) conName parts'
where
ft_bifoldr :: FFoldType (Q (Bool, Exp))
ft_bifoldr = FT {
ft_triv = do lam <- mkSimpleLam2 $ \_ z' -> return z'
return (False, lam)
, ft_var = \v -> return (True, VarE $ tvMap Map.! v)
, ft_tup = \t gs -> do
gg <- sequence gs
lam <- mkSimpleLam2 $ \x z' ->
mkSimpleTupleCase (match_for_con z') t gg x
return (True, lam)
, ft_ty_app = \gs -> do
lam <- mkSimpleLam2 $ \x z' ->
appsE $ varE (foldrArity (length gs))
: map (\(_, hs) -> fmap snd hs) gs
++ map return [z', x]
return (True, lam)
, ft_forall = \_ g -> g
, ft_co_var = \_ -> contravarianceError conName
, ft_fun = \_ _ -> noFunctionsError conName
, ft_bad_app = outOfPlaceTyVarError conName
}
match_for_con :: Exp -> Name -> [(Bool, Exp)] -> Q Match
match_for_con zExp = mkSimpleConMatch2 $ \_ xs -> return $ mkBifoldr xs
where
mkBifoldr :: [Exp] -> Exp
mkBifoldr = foldr AppE zExp
makeBifoldMapMatch :: TyVarMap -> ConstructorInfo -> Q Match
makeBifoldMapMatch tvMap con@(ConstructorInfo{constructorName = conName}) = do
parts <- foldDataConArgs tvMap ft_bifoldMap con
parts' <- sequence parts
match_for_con conName parts'
where
ft_bifoldMap :: FFoldType (Q (Bool, Exp))
ft_bifoldMap = FT {
ft_triv = do lam <- mkSimpleLam $ \_ -> return $ VarE memptyValName
return (False, lam)
, ft_var = \v -> return (True, VarE $ tvMap Map.! v)
, ft_tup = \t gs -> do
gg <- sequence gs
lam <- mkSimpleLam $ mkSimpleTupleCase match_for_con t gg
return (True, lam)
, ft_ty_app = \gs -> do
e <- appsE $ varE (foldMapArity (length gs))
: map (\(_, hs) -> fmap snd hs) gs
return (True, e)
, ft_forall = \_ g -> g
, ft_co_var = \_ -> contravarianceError conName
, ft_fun = \_ _ -> noFunctionsError conName
, ft_bad_app = outOfPlaceTyVarError conName
}
match_for_con :: Name -> [(Bool, Exp)] -> Q Match
match_for_con = mkSimpleConMatch2 $ \_ xs -> return $ mkBifoldMap xs
where
mkBifoldMap :: [Exp] -> Exp
mkBifoldMap [] = VarE memptyValName
mkBifoldMap es = foldr1 (AppE . AppE (VarE mappendValName)) es
makeBitraverseMatch :: TyVarMap -> ConstructorInfo -> Q Match
makeBitraverseMatch tvMap con@(ConstructorInfo{constructorName = conName}) = do
parts <- foldDataConArgs tvMap ft_bitrav con
parts' <- sequence parts
match_for_con conName parts'
where
ft_bitrav :: FFoldType (Q (Bool, Exp))
ft_bitrav = FT {
ft_triv = return (False, VarE pureValName)
, ft_var = \v -> return (True, VarE $ tvMap Map.! v)
, ft_tup = \t gs -> do
gg <- sequence gs
lam <- mkSimpleLam $ mkSimpleTupleCase match_for_con t gg
return (True, lam)
, ft_ty_app = \gs -> do
e <- appsE $ varE (traverseArity (length gs))
: map (\(_, hs) -> fmap snd hs) gs
return (True, e)
, ft_forall = \_ g -> g
, ft_co_var = \_ -> contravarianceError conName
, ft_fun = \_ _ -> noFunctionsError conName
, ft_bad_app = outOfPlaceTyVarError conName
}
match_for_con :: Name -> [(Bool, Exp)] -> Q Match
match_for_con = mkSimpleConMatch2 $ \conExp xs -> return $ mkApCon conExp xs
where
mkApCon :: Exp -> [Exp] -> Exp
mkApCon conExp [] = VarE pureValName `AppE` conExp
mkApCon conExp [e] = VarE fmapValName `AppE` conExp `AppE` e
mkApCon conExp (e1:e2:es) = foldl' appAp
(VarE liftA2ValName `AppE` conExp `AppE` e1 `AppE` e2) es
where appAp se1 se2 = InfixE (Just se1) (VarE apValName) (Just se2)
buildTypeInstance :: BiClass
-> Name
-> Cxt
-> [Type]
-> DatatypeVariant
-> Q (Cxt, Type)
buildTypeInstance biClass tyConName dataCxt instTysOrig variant = do
varTysExp <- mapM resolveTypeSynonyms instTysOrig
let remainingLength :: Int
remainingLength = length instTysOrig - 2
droppedTysExp :: [Type]
droppedTysExp = drop remainingLength varTysExp
droppedStarKindStati :: [StarKindStatus]
droppedStarKindStati = map canRealizeKindStar droppedTysExp
when (remainingLength < 0 || any (== NotKindStar) droppedStarKindStati) $
derivingKindError biClass tyConName
let droppedKindVarNames :: [Name]
droppedKindVarNames = catKindVarNames droppedStarKindStati
varTysExpSubst :: [Type]
varTysExpSubst = map (substNamesWithKindStar droppedKindVarNames) varTysExp
remainingTysExpSubst, droppedTysExpSubst :: [Type]
(remainingTysExpSubst, droppedTysExpSubst) =
splitAt remainingLength varTysExpSubst
droppedTyVarNames :: [Name]
droppedTyVarNames = freeVariables droppedTysExpSubst
unless (all hasKindStar droppedTysExpSubst) $
derivingKindError biClass tyConName
let preds :: [Maybe Pred]
kvNames :: [[Name]]
kvNames' :: [Name]
(preds, kvNames) = unzip $ map (deriveConstraint biClass) remainingTysExpSubst
kvNames' = concat kvNames
remainingTysExpSubst' :: [Type]
remainingTysExpSubst' =
map (substNamesWithKindStar kvNames') remainingTysExpSubst
remainingTysOrigSubst :: [Type]
remainingTysOrigSubst =
map (substNamesWithKindStar (union droppedKindVarNames kvNames'))
$ take remainingLength instTysOrig
isDataFamily :: Bool
isDataFamily = case variant of
Datatype -> False
Newtype -> False
DataInstance -> True
NewtypeInstance -> True
remainingTysOrigSubst' :: [Type]
remainingTysOrigSubst' =
if isDataFamily
then remainingTysOrigSubst
else map unSigT remainingTysOrigSubst
instanceCxt :: Cxt
instanceCxt = catMaybes preds
instanceType :: Type
instanceType = AppT (ConT $ biClassName biClass)
$ applyTyCon tyConName remainingTysOrigSubst'
when (any (`predMentionsName` droppedTyVarNames) dataCxt) $
datatypeContextError tyConName instanceType
unless (canEtaReduce remainingTysExpSubst' droppedTysExpSubst) $
etaReductionError instanceType
return (instanceCxt, instanceType)
deriveConstraint :: BiClass -> Type -> (Maybe Pred, [Name])
deriveConstraint biClass t
| not (isTyVar t) = (Nothing, [])
| otherwise = case hasKindVarChain 1 t of
Just ns -> ((`applyClass` tName) `fmap` biClassConstraint biClass 1, ns)
_ -> case hasKindVarChain 2 t of
Just ns -> ((`applyClass` tName) `fmap` biClassConstraint biClass 2, ns)
_ -> (Nothing, [])
where
tName :: Name
tName = varTToName t
derivingKindError :: BiClass -> Name -> Q a
derivingKindError biClass tyConName = fail
. 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 -> Q a
contravarianceError conName = fail
. showString "Constructor ‘"
. showString (nameBase conName)
. showString "‘ must not use the last type variable(s) in a function argument"
$ ""
noFunctionsError :: Name -> Q a
noFunctionsError conName = fail
. showString "Constructor ‘"
. showString (nameBase conName)
. showString "‘ must not contain function types"
$ ""
datatypeContextError :: Name -> Type -> Q a
datatypeContextError dataName instanceType = fail
. 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 -> Q a
existentialContextError conName = fail
. showString "Constructor ‘"
. showString (nameBase conName)
. showString "‘ must be truly polymorphic in the last argument(s) of the data type"
$ ""
outOfPlaceTyVarError :: Name -> Q a
outOfPlaceTyVarError conName = fail
. showString "Constructor ‘"
. showString (nameBase conName)
. showString "‘ must only use its last two type variable(s) within"
. showString " the last two argument(s) of a data type"
$ ""
etaReductionError :: Type -> Q a
etaReductionError instanceType = fail $
"Cannot eta-reduce to an instance of form \n\tinstance (...) => "
++ pprint instanceType
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
fmapArity :: Int -> Name
fmapArity 1 = fmapValName
fmapArity 2 = bimapValName
fmapArity n = arityErr n
foldrArity :: Int -> Name
foldrArity 1 = foldrValName
foldrArity 2 = bifoldrValName
foldrArity n = arityErr n
foldMapArity :: Int -> Name
foldMapArity 1 = foldMapValName
foldMapArity 2 = bifoldMapValName
foldMapArity n = arityErr n
traverseArity :: Int -> Name
traverseArity 1 = traverseValName
traverseArity 2 = bitraverseValName
traverseArity n = arityErr n
arityErr :: Int -> a
arityErr n = error $ "Unsupported arity: " ++ show n
allowExQuant :: BiClass -> Bool
allowExQuant Bifoldable = True
allowExQuant _ = False
biFunEmptyCase :: BiFun -> Name -> Name -> Q Exp
biFunEmptyCase biFun z value =
biFunTrivial emptyCase
(varE pureValName `appE` emptyCase)
biFun z
where
emptyCase :: Q Exp
emptyCase = caseE (varE value) []
biFunNoCons :: BiFun -> Name -> Name -> Q Exp
biFunNoCons biFun z value =
biFunTrivial seqAndError
(varE pureValName `appE` seqAndError)
biFun z
where
seqAndError :: Q Exp
seqAndError = appE (varE seqValName) (varE value) `appE`
appE (varE errorValName)
(stringE $ "Void " ++ nameBase (biFunName biFun))
biFunTrivial :: Q Exp -> Q Exp -> BiFun -> Name -> Q Exp
biFunTrivial bimapE bitraverseE biFun z = go biFun
where
go :: BiFun -> Q Exp
go Bimap = bimapE
go Bifoldr = varE z
go BifoldMap = varE memptyValName
go Bitraverse = bitraverseE
data FFoldType a
= FT { ft_triv :: a
, ft_var :: Name -> a
, ft_co_var :: Name -> a
, ft_fun :: a -> a -> a
, ft_tup :: TupleSort -> [a] -> a
, ft_ty_app :: [(Type, a)] -> a
, ft_bad_app :: a
, ft_forall :: [TyVarBndrSpec] -> a -> a
}
functorLikeTraverse :: forall a.
TyVarMap
-> FFoldType a
-> Type
-> Q a
functorLikeTraverse tvMap (FT { ft_triv = caseTrivial, ft_var = caseVar
, ft_co_var = caseCoVar, ft_fun = caseFun
, ft_tup = caseTuple, ft_ty_app = caseTyApp
, ft_bad_app = caseWrongArg, ft_forall = caseForAll })
ty
= do ty' <- resolveTypeSynonyms ty
(res, _) <- go False ty'
return res
where
go :: Bool
-> Type
-> Q (a, Bool)
go co t@AppT{}
| (ArrowT, [funArg, funRes]) <- unapplyTy t
= do (funArgR, funArgC) <- go (not co) funArg
(funResR, funResC) <- go co funRes
if funArgC || funResC
then return (caseFun funArgR funResR, True)
else trivial
go co t@AppT{} = do
let (f, args) = unapplyTy t
(_, fc) <- go co f
(xrs, xcs) <- fmap unzip $ mapM (go co) args
let numLastArgs, numFirstArgs :: Int
numLastArgs = min 2 $ length args
numFirstArgs = length args - numLastArgs
tuple :: TupleSort -> Q (a, Bool)
tuple tupSort = return (caseTuple tupSort xrs, True)
wrongArg :: Q (a, Bool)
wrongArg = return (caseWrongArg, True)
case () of
_ | not (or xcs)
-> trivial
| TupleT len <- f
-> tuple $ Boxed len
#if MIN_VERSION_template_haskell(2,6,0)
| UnboxedTupleT len <- f
-> tuple $ Unboxed len
#endif
| fc || or (take numFirstArgs xcs)
-> wrongArg
| otherwise
-> do itf <- isInTypeFamilyApp tyVarNames f args
if itf
then wrongArg
else return ( caseTyApp $ drop numFirstArgs $ zip args xrs
, True )
go co (SigT t k) = do
(_, kc) <- go_kind co k
if kc
then return (caseWrongArg, True)
else go co t
go co (VarT v)
| Map.member v tvMap
= return (if co then caseCoVar v else caseVar v, True)
| otherwise
= trivial
go co (ForallT tvbs _ t) = do
(tr, tc) <- go co t
let tvbNames = map tvName tvbs
if not tc || any (`elem` tvbNames) tyVarNames
then trivial
else return (caseForAll tvbs tr, True)
go _ _ = trivial
go_kind :: Bool
-> Kind
-> Q (a, Bool)
#if MIN_VERSION_template_haskell(2,9,0)
go_kind = go
#else
go_kind _ _ = trivial
#endif
trivial :: Q (a, Bool)
trivial = return (caseTrivial, False)
tyVarNames :: [Name]
tyVarNames = Map.keys tvMap
foldDataConArgs :: forall a. TyVarMap -> FFoldType a -> ConstructorInfo -> Q [a]
foldDataConArgs tvMap ft con = do
fieldTys <- mapM resolveTypeSynonyms $ constructorFields con
mapM foldArg fieldTys
where
foldArg :: Type -> Q a
foldArg = functorLikeTraverse tvMap ft
mkSimpleLam :: (Exp -> Q Exp) -> Q Exp
mkSimpleLam lam = do
n <- newName "n"
body <- lam (VarE n)
return $ LamE [VarP n] body
mkSimpleLam2 :: (Exp -> Exp -> Q Exp) -> Q Exp
mkSimpleLam2 lam = do
n1 <- newName "n1"
n2 <- newName "n2"
body <- lam (VarE n1) (VarE n2)
return $ LamE [VarP n1, VarP n2] body
mkSimpleConMatch :: (Name -> [a] -> Q Exp)
-> Name
-> [Exp -> a]
-> Q Match
mkSimpleConMatch fold conName insides = do
varsNeeded <- newNameList "_arg" $ length insides
let pat = ConP conName (map VarP varsNeeded)
rhs <- fold conName (zipWith (\i v -> i $ VarE v) insides varsNeeded)
return $ Match pat (NormalB rhs) []
mkSimpleConMatch2 :: (Exp -> [Exp] -> Q Exp)
-> Name
-> [(Bool, Exp)]
-> Q Match
mkSimpleConMatch2 fold conName insides = do
varsNeeded <- newNameList "_arg" lengthInsides
let pat = ConP conName (map VarP varsNeeded)
exps = catMaybes $ zipWith (\(m, i) v -> if m then Just (i `AppE` VarE v)
else Nothing)
insides varsNeeded
argTysTyVarInfo = map (\(m, _) -> m) insides
(asWithTyVar, asWithoutTyVar) = partitionByList argTysTyVarInfo varsNeeded
conExpQ
| null asWithTyVar = appsE (conE conName:map varE asWithoutTyVar)
| otherwise = do
bs <- newNameList "b" lengthInsides
let bs' = filterByList argTysTyVarInfo bs
vars = filterByLists argTysTyVarInfo
(map varE bs) (map varE varsNeeded)
lamE (map varP bs') (appsE (conE conName:vars))
conExp <- conExpQ
rhs <- fold conExp exps
return $ Match pat (NormalB rhs) []
where
lengthInsides = length insides
data TupleSort
= Boxed Int
#if MIN_VERSION_template_haskell(2,6,0)
| Unboxed Int
#endif
mkSimpleTupleCase :: (Name -> [a] -> Q Match)
-> TupleSort -> [a] -> Exp -> Q Exp
mkSimpleTupleCase matchForCon tupSort insides x = do
let tupDataName = case tupSort of
Boxed len -> tupleDataName len
#if MIN_VERSION_template_haskell(2,6,0)
Unboxed len -> unboxedTupleDataName len
#endif
m <- matchForCon tupDataName insides
return $ CaseE x [m]