free-foil-0.0.3: src/Control/Monad/Free/Foil/TH/ZipMatch.hs
{-# LANGUAGE LambdaCase #-}
{-# OPTIONS_GHC -fno-warn-type-defaults #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE TemplateHaskell #-}
module Control.Monad.Free.Foil.TH.ZipMatch where
import Language.Haskell.TH
import Control.Monad.Foil.TH.Util
import Control.Monad.Free.Foil
-- | Generate 'ZipMatch' instance for a given bifunctor.
deriveZipMatch
:: Name -- ^ Type name for the signature bifunctor.
-> Q [Dec]
deriveZipMatch signatureT = do
TyConI (DataD _ctx _name signatureTVars _kind signatureCons _deriv) <- reify signatureT
case reverse signatureTVars of
(tvarName -> term) : (tvarName -> scope) : (reverse -> params) -> do
let signatureType = PeelConT signatureT (map (VarT . tvarName) params)
clauses <- concat <$> mapM (toClause scope term) signatureCons
let defaultClause = Clause [WildP, WildP] (NormalB (ConE 'Nothing)) []
let instType = AppT (ConT ''ZipMatch) signatureType
return
[ InstanceD Nothing [] instType
[ FunD 'zipMatch (clauses ++ [defaultClause]) ]
]
_ -> fail "cannot generate pattern synonyms"
where
toClause :: Name -> Name -> Con -> Q [Clause]
toClause scope term = go
where
go = \case
NormalC conName types -> mkClause conName types
RecC conName types -> go (NormalC conName (map removeName types))
InfixC l conName r -> go (NormalC conName [l, r])
ForallC _ _ con -> go con
GadtC conNames types _retType -> concat <$> mapM (\conName -> mkClause conName types) conNames
RecGadtC conNames types retType -> go (GadtC conNames (map removeName types) retType)
mkClause :: Name -> [BangType] -> Q [Clause]
mkClause conName types = return
[Clause [ConP conName [] lpats, ConP conName [] rpats]
(NormalB (AppE (ConE 'Just) (foldl AppE (ConE conName) args))) []]
where
(lpats, rpats, args) = unzip3
[ case type_ of
VarT typeName
| typeName `elem` [scope, term] -> (VarP l, VarP r, TupE [Just (VarE l), Just (VarE r)])
_ -> (VarP l, WildP, VarE l)
| (i, (_bang, type_)) <- zip [0..] types
, let l = mkName ("l" ++ show i)
, let r = mkName ("r" ++ show i)
]