on master help sign in
Add lookup of FunArgs for global names to Elab

This makes it possible to write elaborators for term languages that use Idris's plicity information. As a test of this, the test suite now includes a mini-implementation of a subset of Agda's reflection using Elab.

commitf7e1990ce45a6aeb5cb2eaf62de718c6dc421e53 authorDavid Raymond Christiansen <david@davidchristiansen.dk> dateFri 16 Oct 2015, 18:55 (11 years ago) parent6a208f7
6 files changed+303−6
libs/prelude/Language/Reflection/Elab.idr+18−2 src/Idris/Elab/Term.hs+28−4 src/Idris/Reflection.hs+6 test/meta002/AgdaStyleReflection.idr+247 test/meta002/expected+3 test/meta002/run+1
libs/prelude/Language/Reflection/Elab.idr+18−2
@@ -34,7 +34,9 @@ data Plicity =
3434||| Function arguments
3535|||
3636||| These are the simplest representation of argument lists, and are
37−||| used for functions.
37+||| used for functions. Additionally, because a FunArg provides enough
38+||| information to build an application, a generic type lookup of a
39+||| top-level identifier will return its FunArgs, if applicable.
3840record FunArg where
3941 constructor MkFunArg
4042 name : TTName
@@ -114,6 +116,7 @@ data Elab : Type -> Type where
114116 prim__Guess : Elab (Maybe TT)
115117 prim__LookupTy : TTName -> Elab (List (TTName, NameType, TT))
116118 prim__LookupDatatype : TTName -> Elab (List Datatype)
119+ prim__LookupArgs : TTName -> Elab (List (TTName, List FunArg, Raw))
117120
118121 prim__Check : List (TTName, Binder TT) -> Raw -> Elab (TT, TT)
119122
@@ -185,7 +188,7 @@ namespace Tactics
185188 ||| Halt elaboration with an error
186189 fail : List ErrorReportPart -> Elab a
187190 fail err = prim__Fail err
188−
191+
189192 ||| Look up the lexical binding at the focused hole. Fails if no holes are present.
190193 getEnv : Elab (List (TTName, Binder TT))
191194 getEnv = prim__Env
@@ -228,6 +231,19 @@ namespace Tactics
228231 [] => fail [TextPart "No datatype named", NamePart n]
229232 xs => fail [TextPart "More than one datatype named", NamePart n]
230233
234+
235+ ||| Get the argument specification for each overloading of a name.
236+ lookupArgs : TTName -> Elab (List (TTName, List FunArg, Raw))
237+ lookupArgs n = prim__LookupArgs n
238+
239+ ||| Get the argument specification for a name. Fail if the name does
240+ ||| not uniquely resolve.
241+ lookupArgsExact : TTName -> Elab (TTName, List FunArg, Raw)
242+ lookupArgsExact n = case !(lookupArgs n) of
243+ [res] => return res
244+ [] => fail [NamePart n, TextPart "is not defined."]
245+ xs => fail [NamePart n, TextPart "is ambiguous."]
246+
231247 ||| Attempt to type-check a term, getting back itself and its type.
232248 |||
233249 ||| @ env the environment within which to check the type
src/Idris/Elab/Term.hs+28−4
@@ -31,7 +31,7 @@ import Control.Monad.State.Strict
3131import Data.Foldable (for_)
3232import Data.List
3333import qualified Data.Map as M
34−import Data.Maybe (mapMaybe, fromMaybe, catMaybes)
34+import Data.Maybe (mapMaybe, fromMaybe, catMaybes, maybeToList)
3535import qualified Data.Set as S
3636import qualified Data.Text as T
3737
@@ -1832,9 +1832,9 @@ runElabAction ist fc env tm ns = do tm' <- eval tm
18321832 | n == tacN "prim__LookupTy", [n] <- args
18331833 = do n' <- reifyTTName n
18341834 ctxt <- get_context
1835− let getNameTypeAndType = \case Function ty _ -> (Ref, ty)
1836− TyDecl nt ty -> (nt, ty)
1837− Operator ty _ _ -> (Ref, ty)
1835+ let getNameTypeAndType = \case Function ty _ -> (Ref, ty)
1836+ TyDecl nt ty -> (nt, ty)
1837+ Operator ty _ _ -> (Ref, ty)
18381838 CaseOp _ ty _ _ _ _ -> (Ref, ty)
18391839 -- Idris tuples nest to the right
18401840 reflectTriple (x, y, z) =
@@ -1858,6 +1858,30 @@ runElabAction ist fc env tm ns = do tm' <- eval tm
18581858 fmap fst . checkClosed $
18591859 rawList (Var (tacN "Datatype"))
18601860 (map reflectDatatype (buildDatatypes ist n'))
1861+ | n == tacN "prim__LookupArgs", [name] <- args
1862+ = do n' <- reifyTTName name
1863+ let listTy = Var (sNS (sUN "List") ["List", "Prelude"])
1864+ listFunArg = RApp listTy (Var (tacN "FunArg"))
1865+ -- Idris tuples nest to the right
1866+ let reflectTriple (x, y, z) =
1867+ raw_apply (Var pairCon) [ Var (reflm "TTName")
1868+ , raw_apply (Var pairTy) [listFunArg, Var (reflm "Raw")]
1869+ , x
1870+ , raw_apply (Var pairCon) [listFunArg, Var (reflm "Raw")
1871+ , y, z]]
1872+ let out =
1873+ [ reflectTriple (reflectName fn, reflectList (Var (tacN "FunArg")) (map reflectArg args), reflectRaw res)
1874+ | (fn, pargs) <- lookupCtxtName n' (idris_implicits ist)
1875+ , (args, res) <- getArgs pargs . forget <$>
1876+ maybeToList (lookupTyExact fn (tt_ctxt ist))
1877+ ]
1878+
1879+ fmap fst . checkClosed $
1880+ rawList (raw_apply (Var pairTy) [Var (reflm "TTName")
1881+ , raw_apply (Var pairTy) [ RApp listTy
1882+ (Var (tacN "FunArg"))
1883+ , Var (reflm "Raw")]])
1884+ out
18611885 | n == tacN "prim__SourceLocation", [] <- args
18621886 = fmap fst . checkClosed $
18631887 reflectFC fc
src/Idris/Reflection.hs+6
@@ -1047,6 +1047,12 @@ envTupleType
10471047 , (RApp (Var $ reflm "Binder") (Var $ reflm "TT"))
10481048 ]
10491049
1050+reflectList :: Raw -> [Raw] -> Raw
1051+reflectList ty [] = RApp (Var (sNS (sUN "Nil") ["List", "Prelude"])) ty
1052+reflectList ty (x:xs) = RApp (RApp (RApp (Var (sNS (sUN "::") ["List", "Prelude"])) ty)
1053+ x)
1054+ (reflectList ty xs)
1055+
10501056-- | Apply Idris's implicit info to get a signature. The [PArg] should
10511057-- come from a lookup in idris_implicits on IState.
10521058getArgs :: [PArg] -> Raw -> ([RFunArg], Raw)
test/meta002/AgdaStyleReflection.idrnew file+247
@@ -0,0 +1,247 @@
1+||| Implement an Agda-style reflection system as a DSL inside of
2+||| Idris's reflected elaboration. Requires handling type class
3+||| resolution and implicit arguments - some of the same features as
4+||| the Idris elaborator itself.
5+module AgdaStyleReflection
6+
7+
8+import Language.Reflection.Utils
9+import Pruviloj
10+
11+%default total
12+
13+
14+||| Function arguments as provided at the application site
15+record Arg a where
16+ constructor MkArg
17+ plicity : Plicity
18+ argValue : a
19+
20+instance Functor Arg where
21+ map f (MkArg plic x) = MkArg plic (f x)
22+
23+||| Reflected terms, in the tradition of Agda's reflection library
24+data Term
25+ = Var Nat (List (Arg Term))
26+ | Ctor TTName (List (Arg Term))
27+ | TyCtor TTName (List (Arg Term))
28+ | Def TTName (List (Arg Term))
29+ | Lam Term
30+ | Pi Term Term
31+ | Constant Const
32+ | Ty
33+
34+unApply : Raw -> (Raw, List Raw)
35+unApply tm = unApply' tm []
36+ where unApply' : Raw -> List Raw -> (Raw, List Raw)
37+ unApply' (RApp f x) xs = unApply' f (x::xs)
38+ unApply' notApp xs = (notApp, xs)
39+
40+||| Quote a reflected Idris term to a Term in some induced local context
41+covering
42+quoteTerm' : List TTName -> Raw -> Elab Term
43+quoteTerm' env (Var n) =
44+ case findIndex (==n) env of
45+ Just i => return (Var i [])
46+ Nothing =>
47+ case findIndex (\(n', _) => n == n') !getEnv of
48+ Just i => return (Var i [])
49+ Nothing => do [(n', nt, ty)] <- lookupTy n
50+ | [] => fail [TextPart "No such variable", NamePart n]
51+ | vs => fail ([TextPart "Can't disambiguate"] ++
52+ map (NamePart . fst) vs)
53+ case nt of
54+ Bound => fail [TextPart "Bound variable not found"]
55+ Ref => pure $ Def n' []
56+ DCon _ _ => pure $ Ctor n' []
57+ TCon _ _ => pure $ TyCtor n' []
58+quoteTerm' env (RBind n (Lam ty) tm) = Lam <$> quoteTerm' (n::env) tm
59+quoteTerm' env (RBind n (Pi ty kind) tm) = [| Pi (quoteTerm' env ty)
60+ (quoteTerm' (n::env) tm) |]
61+quoteTerm' env (RBind n (Let ty val) tm) = fail [TextPart "can't quote let"]
62+quoteTerm' env (RBind n (Hole ty) tm) = fail [TextPart "can't quote hole"]
63+quoteTerm' env (RBind n (GHole ty) tm) = fail [TextPart "can't quote deferred hole"]
64+quoteTerm' env (RBind n (Guess ty val) tm) = fail [TextPart "can't quote guess"]
65+quoteTerm' env (RBind n (PVar ty) tm) = fail [TextPart "can't quote patvar"]
66+quoteTerm' env (RBind n (PVTy ty) tm) = fail [TextPart "can't quote pattern type"]
67+quoteTerm' env (RApp tm tm') =
68+ do let (f, args) = unApply (RApp tm tm')
69+ rawArgs <- traverse (quoteTerm' env) args
70+ case f of
71+ Var n => doApp n rawArgs
72+ tm => fail [RawPart tm, TextPart "not normalized first"]
73+
74+ where
75+ mkArgs : List Term -> List FunArg -> List (Arg Term)
76+ mkArgs [] _ = []
77+ mkArgs tms [] = map (MkArg Explicit) tms
78+ mkArgs (tm::tms) (ar::ars) = MkArg (plicity ar) tm :: mkArgs tms ars
79+
80+ doApp : TTName -> List Term -> Elab Term
81+ doApp fn xs =
82+ do (fn', nt, ty) <- lookupTyExact fn
83+ (_, args, res) <- lookupArgsExact fn'
84+ let rest = mkArgs xs args
85+ case nt of
86+ Bound => fail [TextPart "Bound variable illegal here"]
87+ Ref => pure $ Def fn' rest
88+ DCon _ _ => pure $ Ctor fn' rest
89+ TCon _ _ => pure $ TyCtor fn' rest
90+
91+quoteTerm' env RType = pure Ty
92+quoteTerm' env (RUType x) = fail [TextPart "Uniqueness not supported here"]
93+quoteTerm' env (RConstant c) = pure (Constant c)
94+
95+||| Quote a reflected Idris term to a Term
96+covering
97+quoteTerm : Raw -> Elab Term
98+quoteTerm = quoteTerm' []
99+
100+||| Attempt to resolve a de Bruijn index in a context
101+resolveVar : Nat -> Elab TTName
102+resolveVar k =
103+ do env <- getEnv
104+ getVar k (map fst env)
105+
106+ where getVar : Nat -> List TTName -> Elab TTName
107+ getVar _ [] = fail [TextPart "Variable out of scope"]
108+ getVar Z (n :: _ ) = return n -- NB assumes unique names!
109+ getVar (S k) (_ :: env) = getVar k env
110+
111+||| Prepare to apply a global by matching its argument plicities to
112+||| its argument list.
113+prepareApply : List FunArg -> List (Arg Term) -> Elab (List (Arg (Maybe Term)))
114+prepareApply [] tms = pure $ map (map Just) tms -- overapplied OK - it may return a function!
115+prepareApply ars [] = pure [] -- underapplied is ok too
116+prepareApply (MkFunArg _ _ Constraint _ :: ars) (MkArg Constraint tm :: tms) =
117+ [| (pure (MkArg Constraint (Just tm))) :: prepareApply ars tms |]
118+prepareApply (MkFunArg _ _ Constraint _ :: ars) (MkArg plic tm :: tms) =
119+ [| (pure (MkArg Constraint Nothing)) :: prepareApply ars (MkArg plic tm :: tms) |]
120+prepareApply (MkFunArg _ _ Implicit _ :: ars) (MkArg Implicit tm :: tms) =
121+ [| (pure (MkArg Implicit (Just tm))) :: prepareApply ars tms |]
122+prepareApply (MkFunArg _ _ Implicit _ :: ars) (MkArg plic tm :: tms) =
123+ [| (pure (MkArg Implicit Nothing)) :: prepareApply ars (MkArg plic tm :: tms) |]
124+prepareApply (MkFunArg _ _ Explicit _ :: ars) (MkArg Explicit tm :: tms) =
125+ [| (pure (MkArg Explicit (Just tm))) :: prepareApply ars tms |]
126+prepareApply (MkFunArg _ _ Explicit _ :: ars) (MkArg plic tm :: tms) =
127+ fail [TextPart "Expected explicit argument, got something else!"]
128+
129+||| Should this argument be solved by unification?
130+toSolve : Arg (Maybe Term) -> Bool
131+toSolve (MkArg Explicit _) = False
132+toSolve (MkArg _ (Just _)) = False
133+toSolve _ = True
134+
135+mutual
136+ covering
137+ applyGlobal : List TTName -> TTName -> List (Arg Term) -> Elab ()
138+ applyGlobal locals fn args =
139+ do (fn', argSpec, _) <- lookupArgsExact fn
140+ todo <- prepareApply argSpec args
141+ hs <- apply (Var fn') (map toSolve todo)
142+ solve
143+ for_ {b=()} (zip todo hs) $ \(MkArg p tm, h) =>
144+ case p of
145+ Constraint => ignore $ inHole h (resolveTC `{applyGlobal})
146+ _ => case tm of
147+ Nothing => skip
148+ Just arg => ignore $ inHole h $ spliceTerm' locals arg
149+
150+ covering
151+ spliceTerm' : List TTName -> Term -> Elab ()
152+ spliceTerm' locals (Var k xs) =
153+ do n <- resolveVar k
154+ -- local bindings can't have non-explicit args, so just do a normal application
155+ hs <- apply (Var n) (map (const True) xs)
156+ solve
157+ for_ {b = ()} (zip hs xs) $ \(h, MkArg _ x) =>
158+ ignore $ inHole h (spliceTerm' locals x)
159+ spliceTerm' locals (Ctor n xs) = applyGlobal locals n xs
160+ spliceTerm' locals (TyCtor n xs) = applyGlobal locals n xs
161+ spliceTerm' locals (Def n xs) = applyGlobal locals n xs
162+ spliceTerm' locals (Lam body) =
163+ do x <- gensym "x"
164+ attack
165+ intro x
166+ spliceTerm' (x::locals) body
167+ solve
168+ spliceTerm' locals (Pi ty body) =
169+ do arg <- gensym "arg"
170+ claim arg `(Type)
171+ x <- gensym "x"
172+ attack
173+ forall x (Var arg)
174+ focus arg; spliceTerm' locals ty
175+ spliceTerm' (x::locals) body
176+ solve
177+ spliceTerm' locals (Constant c) =
178+ do fill (quote c); solve
179+ spliceTerm' locals Ty =
180+ do fill `(Type); solve
181+
182+covering
183+spliceTerm : Term -> Elab ()
184+spliceTerm = spliceTerm' []
185+
186+||| Lift an Agda-style tactic into an Idris-style tactic
187+covering
188+quoteGoalImpl : (Term -> Term) -> Elab ()
189+quoteGoalImpl f = do compute
190+ g <- goalType >>= quoteTerm
191+ spliceTerm (f g)
192+
193+syntax "quoteGoal" {x} "in" [expr] = %runElab (quoteGoalImpl (\x => expr))
194+
195+syntax "tactic" [expr] = %runElab (quoteGoalImpl expr)
196+
197+
198+
199+
200+
201+||| A simple proof search tactic with failure
202+|||
203+||| @ goal the type to fill out with search
204+covering
205+trivial' : (goal : Term) -> Maybe Term
206+trivial' (TyCtor `{Nat} []) = pure $ Ctor `{Z} []
207+trivial' (TyCtor `{Unit} []) = pure $ Ctor `{MkUnit} []
208+trivial' (TyCtor `{Pair} [MkArg Explicit a, MkArg Explicit b]) =
209+ pure $
210+ Ctor `{MkPair}
211+ [ MkArg Explicit !(trivial' a)
212+ , MkArg Explicit !(trivial' b)
213+ ]
214+trivial' (TyCtor `{Either} [MkArg Explicit a, MkArg Explicit b]) =
215+ (left <$> trivial' a) <|> (right <$> trivial' b)
216+ where left : Term -> Term
217+ left x = Ctor `{Left} [MkArg Explicit x]
218+ right : Term -> Term
219+ right x = Ctor `{Right} [MkArg Explicit x]
220+trivial' (Pi _ body) = Lam <$> trivial' body
221+trivial' _ = empty
222+
223+||| Attempt proof search, or fake an error message if it fails.
224+perhaps : Maybe Term -> Term
225+perhaps = maybe (Ctor (UN "PROOF SEARCH FAILURE") []) id
226+
227+||| A simple proof search tactic
228+|||
229+||| @ goal the type to fill out with search
230+covering
231+trivial : (goal : Term) -> Term
232+trivial = perhaps . trivial'
233+
234+foo : (Nat, (), Nat, Either (() -> ()) Void)
235+foo = quoteGoal x in trivial x
236+
237+bar : (Nat, (), Either Void Nat, Nat -> ())
238+bar = tactic trivial
239+
240+-- When checking right hand side of baz:
241+-- PROOF SEARCH FAILURE is not defined.
242+baz : (Nat, Void)
243+baz = tactic trivial
244+
245+-- Local Variables:
246+-- idris-load-packages: ("pruviloj")
247+-- End:
test/meta002/expected+3
@@ -1,3 +1,6 @@
11Tacs.idr:298:15:
22When checking right hand side of testElab3:
33Unifying ty and ARR ty t would lead to infinite value
4+AgdaStyleReflection.idr:243:5:
5+When checking right hand side of baz:
6+PROOF SEARCH FAILURE is not defined.
test/meta002/run+1
@@ -1,3 +1,4 @@
11#!/usr/bin/env bash
22idris $@ -p pruviloj --nocolour --check --consolewidth 70 Tacs.idr
3+idris $@ -p pruviloj --nocolour --check --consolewidth 70 AgdaStyleReflection.idr
34rm -f *.ibc