Source code on Github
{-# OPTIONS -v allTactics:100 #-}
{-# OPTIONS --safe --without-K #-}
open import Meta.Init
module Tactic.Derive (className : Name) (projName : Name) where
open import Meta.Prelude
open import Agda.Builtin.Reflection using () renaming (primShowQName to showName)
import Data.Bool.ListAction as L
import Data.List as L hiding (any)
import Data.List.NonEmpty as NE
import Data.String as S
open import Reflection.Tactic
open import Reflection.Utils
open import Reflection.Utils.TCI
open import Relation.Nullary.Decidable
open import Tactic.ClauseBuilder
open import Class.DecEq
open import Class.Functor
open import Class.Monad
open import Class.MonadReader.Instances
open import Class.MonadTC.Instances
open import Class.Show
open import Class.Traversable
instance
_ = ContextMonad-MonadTC
_ = Functor-M {M = TC}
_ = Show-List
_ = DecEq-×
open ClauseExprM
WrapperChain : Set
WrapperChain = List Name
applyChain : WrapperChain → Term → Term
applyChain [] t = t
applyChain (n ∷ ns) t = n ∙⟦ applyChain ns t ⟧
matchesChain : WrapperChain → Name → Term → Bool
matchesChain [] dName (def n _) = ⌊ dName ≟ n ⌋
matchesChain [] _ _ = false
matchesChain (n ∷ ns) dName (def n' args) =
⌊ n ≟ n' ⌋ ∧ L.any (λ where (arg _ t) → matchesChain ns dName t) args
matchesChain _ _ _ = false
genClassType : ℕ → Name → Maybe WrapperChain → TC Type
genClassType arity dName wName = do
params ← getParamsAndIndices dName
let params' = L.map (λ where (abs x y) → abs x (hide y)) $ take (length params ∸ arity) params
sorts ← collectRelevantSorts params'
debugLog1 ("Generate required instances at indices: " S.++ show (L.map proj₁ sorts))
let adjustedDBs = L.zipWith (λ (i , tel) a → (i + a , tel)) sorts (upTo (length sorts))
adjustedDBs' ← extendContext' (toTelescope params) $ genSortInstanceWithCtx adjustedDBs
let adjParams = params' ++ adjustedDBs'
debugLog1 "AdjustedParams: "
logTelescope (L.map ((λ where (abs s x) → just s , x)) adjParams)
ty ← applyWithVisibility dName (L.map (♯ ∘ (_+ length sorts)) (downFrom (length params)))
return $ modifyClassType wName (adjParams , ty)
where
collectRelevantSorts : List (Abs (Arg Type)) → TC (List (ℕ × ℕ))
collectRelevantSorts [] = return []
collectRelevantSorts (abs x (arg _ t) ∷ l) = do
rec ← extendContext (x , hArg t) $ collectRelevantSorts l
(b , k) ← isNArySort t
return (if b then (length l , k) ∷ rec else rec)
genSortInstance : ℕ → ℕ → ℕ → TC Term
genSortInstance k 0 i = do
res ← applyWithVisibilityDB (i + k) (L.map ♯ $ downFrom k)
return $ className ∙⟦ res ⟧
genSortInstance k (suc a) i = do
res ← extendContext ("" , hArg unknown) $ genSortInstance k a i
return $ pi (hArg unknown) $ abs "_" res
genSortInstanceWithCtx : List (ℕ × ℕ) → TC (List (Abs (Arg Term)))
genSortInstanceWithCtx [] = return []
genSortInstanceWithCtx ((i , k) ∷ xs) = do
x' ← (abs "_" ∘ iArg) <$> (genSortInstance k k i)
(x' ∷_) <$> (extendContext ("", hArg unknown) $ genSortInstanceWithCtx xs)
modifyClassType : Maybe WrapperChain → TypeView → Type
modifyClassType nothing (tel , ty) = tyView (tel , className ∙⟦ ty ⟧)
modifyClassType (just ns) (tel , ty) = tyView (tel , className ∙⟦ applyChain ns ty ⟧)
TransEntry : Set
TransEntry = (WrapperChain × Name) × Name
lookupByTerm : List TransEntry → Term → Maybe Name
lookupByTerm l ty = proj₂ <$> L.findᵇ (λ where ((c , t) , _) → matchesChain c t ty) l
private module AllChainsTo (ns : List Name) where
nameInSeeds : Name → Bool
nameInSeeds n' = L.any (_≡ᵇ n') ns
mutual
chainsTo : Term → List (WrapperChain × Name)
chainsTo (def n' args) with nameInSeeds n'
... | true = [ ([] , n') ]
... | false = L.map (λ where (c , t) → (n' ∷ c , t)) (chainsToArgs args)
chainsTo _ = []
chainsToArgs : List (Arg Term) → List (WrapperChain × Name)
chainsToArgs [] = []
chainsToArgs (arg _ t ∷ rest) = chainsTo t ++ chainsToArgs rest
mutual
allChainsTo : Term → List (WrapperChain × Name)
allChainsTo t@(def n' args) with nameInSeeds n'
... | true = []
... | false = chainsTo t ++ allChainsToArgs args
allChainsTo _ = []
allChainsToArgs : List (Arg Term) → List (WrapperChain × Name)
allChainsToArgs [] = []
allChainsToArgs (arg _ t ∷ rest) = allChainsTo t ++ allChainsToArgs rest
open AllChainsTo using (allChainsTo)
genMutualHelpers : List Name → TC (List (WrapperChain × Name))
genMutualHelpers ns = do
tysPerSeed ← traverse
(λ n → L.map (unArg ∘ unAbs) <$> (L.concatMap (proj₁ ∘ viewTy ∘ proj₂) <$> getConstrs n)) ns
return $ deduplicate _≟_ $ L.concatMap (allChainsTo ns) $ concat tysPerSeed
module _ (arity : ℕ) (genCe : (Term → Maybe Name) → List SinglePattern → List (NE.List⁺ SinglePattern × TC (ClauseExpr ⊎ Maybe Term))) where
deriveSingle : List TransEntry → Name → Name → Maybe WrapperChain → TC (Arg Name × Type × List Clause)
deriveSingle transName tgtData iName wChain = inDebugPath "DeriveSingle" do
debugLog ("For: " ∷ᵈ tgtData ∷ᵈ [])
goalTy ← genClassType arity tgtData wChain
let outerName = maybe (λ where [] → tgtData ; (x ∷ _) → x) tgtData wChain
ps ← constructorPatterns' (outerName ∙)
cs ← local (λ c → record c { goal = inj₂ goalTy }) $
singleMatchExpr ([] , iArg (Pattern.proj projName)) $ contMatch $ multiMatchExprM $
genCe (lookupByTerm transName) ps
let defName = maybe (λ _ → vArg iName) (iArg iName) wChain
return (defName , goalTy , clauseExprToClauses cs)
deriveGroup : List (Name × Name) → TC (List (Arg Name × Type × List Clause))
deriveGroup seeds = do
helpers ← genMutualHelpers $ L.map proj₁ seeds
helperNames ← traverse ⦃ Functor-List ⦄ mkHelperName helpers
let helperTable : List TransEntry
helperTable = L.zip helpers helperNames
seedTable : List TransEntry
seedTable = L.map (λ (s , i) → (([] , s) , i)) seeds
open D (seedTable ++ helperTable)
baseResults ← traverse deriveBase seeds
helperResults ← traverse deriveHelper helperTable
return (baseResults ++ helperResults)
where
mkHelperName : WrapperChain × Name → TC Name
mkHelperName (chain , tgt) = freshName
(showName className
S.++ L.foldr (λ n s → "-" S.++ showName n S.++ s) "" chain
S.++ "-" S.++ showName tgt)
module D (transName : List (TransEntry)) where
deriveBase : Name × Name → TC (Arg Name × Type × List Clause)
deriveBase (s , i) = deriveSingle transName s i nothing
deriveHelper : TransEntry → TC (Arg Name × Type × List Clause)
deriveHelper ((chain , tgt) , n) = deriveSingle transName tgt n (just chain)
derive-Class : ⦃ TCOptions ⦄ → List (Name × Name) → UnquoteDecl
derive-Class l = initUnquoteWithGoal (className ∙) $
declareAndDefineFuns =<< runAndReset (deriveGroup l)