on bc8da8d help sign in
Idris-dev/docs/reference/elaborator-reflection.rst
202 lines 10 kB history raw
1.. _elaborator-reflection:
3*********************
4Elaborator Reflection
5*********************
7The Idris elaborator is responsible for converting high-level Idris code into the core language.
8It is implemented as a kind of embedded tactic language in Haskell, where tactic scripts are written in an *elaboration monad* that provides error handling and a proof state.
9For details, see Edwin Brady's 2013 paper in the Journal of Functional Programming.
11Elaborator reflection makes the elaboration type as well as a selection of its tactics available to Idris code.
12This means that metaprograms written in Idris can have complete control over the elaboration process, generating arbitrary code, and they have access to all of the facilities available in the elaborator, such as higher-order unification, type checking, and emitting auxiliary definitions.
14The Elaborator State
15====================
17The elaborator state contains information about the ongoing elaboration process.
18In particular, it contains a *goal type*, which is to be filled by an under-construction *proof term*.
19The proof term can contain *holes*, each of which has a scope in which it is valid and a type.
20Some holes may additionally contain *guesses*, which can be substituted in the scope of the hole.
21The holes are tracked in a *hole queue*, and one of them is *focused*.
22In addition to the goal type, proof term, and holes, the elaborator state contains a collection of unsolved unification problems that can affect elaboration.
24The elaborator state is not directly available to Idris programs.
25Instead, it is modified through the use of *tactics*, which are operations that affect the elaborator state.
26A tactic that returns a value of type ``a``, potentially modifying the elaborator state, has type ``Elab a``.
27The default tactics are all in the namespace ``Language.Reflection.Elab.Tactics``.
30Running Elaborator Scripts
31==========================
33On their own, tactics have no effect.
34The meta-operation ``%runElab script`` runs ``script`` in the current elaboration context.
35Before you can use ``%runElab``, you will have to enable the language extension by adding
36``%language ElabReflection`` in your file (or by passing ``-X ElabReflection`` to the
37``idris`` executable from your command line).
38For example, the following script constructs the identity function at type ``Nat``:
40.. code-block:: idris
42 idNat : Nat -> Nat
43 idNat = %runElab (do intro `{{x}}
44 fill (Var `{{x}})
45 solve)
48On the right-hand side, the Idris elaborator has the goal ``Nat -> Nat``.
49When it encounters the ``%runElab`` directive, it fulfills this goal by running the provided script.
50The first tactic, ``intro``, constructs a lambda that binds the name ``x``.
51The name argument is optional because a default name can be taken from the function type.
52Now, the proof term is of the form ``\x : Nat => {hole}``.
53The second tactic, ``fill``, fills this hole with a guess, giving the term ``\x : Nat => {hole≈x}``.
54Finally, the ``solve`` tactic instantiates the guess, giving the result ``\x : Nat => x``.
56Because elaborator scripts are ordinary Idris expressions, it is also possible to use them in multiple contexts.
57Note that there is nothing ``Nat``-specific about the above script.
58We can generate identity functions at any concrete type using the same script:
60.. code-block:: idris
62 mkId : Elab ()
63 mkId = do intro `{{x}}
64 fill (Var `{{x}})
65 solve
67 idNat : Nat -> Nat
68 idNat = %runElab mkId
70 idUnit : () -> ()
71 idUnit = %runElab mkId
73 idString : String -> String
74 idString = %runElab mkId
77Interactively Building Elab Scripts
78===================================
80You can build an ``Elab`` script interactively at the REPL.
81Use the command ``:metavars``, or ``:m`` for short, to list the available holes.
82Then, issue the ``:elab <hole>`` command at the REPL
83to enter the elaboration shell.
85At the shell, you can enter proof tactics to alter the proof state.
86You can view the system-provided tactics prior to entering the shell
87by issuing the REPL command ``:browse Language.Reflection.Elab.Tactics``.
88When you have discharged all goals, you can complete the proof
89using the ``:qed`` command and receive in return an elaboration script
90that fills the hole.
92The interactive elaboration shell accepts a limited number of commands,
93including a subset of the commands understood by the normal Idris REPL
94as well as some elaboration-specific commands. It also supports the
95``do``-syntax, meaning you can write ``res <- command`` to bind the result of
96``command`` to variable ``res``.
98General-purpose commands:
100- ``:eval <EXPR>``, or ``:e <EXPR>`` for short, evaluates the provided expression
101 and prints the result.
103- ``:type <EXPR>``, or ``:t <EXPR>`` for short, prints the provided expression
104 together with its type.
106- ``:search <TYPE>`` searches for definitions having the provided type.
108- ``:doc <NAME>`` searches for definitions with the provided name and prints their
109 documentation.
112Commands for viewing the proof state:
114- ``:state`` displays the current state of the term being constructed. It lists both
115 other goals and the current goal.
117- ``:term`` displays the current proof term as well as its yet-to-be-filled holes.
120Commands for manipulating the proof state:
122- ``:undo`` undoes the effects of the last tactic.
124- ``:abandon`` gives up on proving the current lemma and quits the elaboration shell.
126- ``:qed`` finishes the script and exits the elaboration shell. The shell will only accept
127 this command once it reports, "No more goals." On exit, it will print out the finished
128 elaboration script for you to copy into your program.
131Failure
132=======
134Some tactics may *fail*.
135For example, ``intro`` will fail if the focused hole does not have a function type, ``solve`` will fail if the current hole does not contain a guess, and ``fill`` will fail if the term to be filled in has the wrong type.
136Scripts can also fail explicitly using the ``fail`` tactic.
138To account for failure, there is an ``Alternative`` implementation for ``Elab``.
139The ``<|>`` operator first tries the script to its left.
140If that script fails, any changes that it made to the state are undone and the right argument is executed.
141If the first argument succeeds, then the second argument is not executed.
143Querying the Elaboration State
144==============================
146``Elab`` includes operations to query the elaboration state, allowing scripts to use information about their environment to steer the elaboration process.
147The ordinary Idris bind syntax can be used to propagate this information.
148For example, a tactic that solves the current goal when it is the unit type might look like this:
150.. code-block:: idris
152 triv : Elab ()
153 triv = do compute
154 g <- getGoal
155 case (snd g) of
156 `(() : Type) => do fill `(() : ())
157 solve
158 otherGoal => fail [ TermPart otherGoal
159 , TextPart "is not trivial"
160 ]
163The tactic ``compute`` normalises the type of its goal with respect to the current context.
164While not strictly necessary, this allows ``triv`` to be used in contexts where the triviality of the goal is not immediately apparent.
165Then, ``getGoal`` is used, and its result is bound to ``g``.
166Because it returns a pair consisting of the current goal's name and type, we case-split on its second projection.
167If the goal type turns out to have been the unit type, we fill using the unit constructor and solve the goal.
168Otherwise, we fail with an error message informing the user that the current goal is not trivial.
170Additionally, the elaboration state can be dumped into an error message with the ``debug`` tactic.
171A variant, ``debugMessage``, allows arbitrary messages to be included with the state, allowing for a kind of "``printf`` debugging" of elaboration scripts.
172The message format used by ``debugMessage`` is the same for errors produced by the error reflection mechanism, allowing the re-use of the Idris pretty-printer when rendering messages.
174Changing the Global Context
175===========================
177``Elab`` scripts can modify the global context during execution.
178Just as the Idris elaborator produces auxiliary definitions to implement features such as ``where``-blocks and ``case`` expressions, user elaboration scripts may need to define functions.
179Furthermore, this allows ``Elab`` reflection to be used to implement features such as interface deriving.
180The operations ``declareType``, ``defineFunction``, and ``addImplementation`` allow ``Elab`` scripts to modify the global context.
182Using Idris's Features
183======================
185The Idris compiler has a number of ways to automate the construction of terms.
186On its own, the ``Elab`` state and its interactions with the unifier allow implicits to be solved using unification.
187Additional operations use further features of Idris.
188In particular, ``resolveTC`` solves the current goal using interface resolution, ``search`` invokes the proof search mechanism, and ``sourceLocation`` finds the context in the original file at which the elaboration script is invoked.
191Recursive Elaboration
192=====================
194The elaboration mechanism can be invoked recursively using the ``runElab`` tactic.
195This tactic takes a goal type and an elaboration script as arguments and runs the script in a fresh lexical environment to create an inhabitant of the provided goal type.
196This is primarily useful for code generation, particularly for generating pattern-matching clauses, where variable scope needs to be one that isn't the present local context.
198Learn More
199==========
200While this documentation is still incomplete, elaboration reflection works in Idris today.
201As you wait for the completion of the documentation, the list of built-in tactics can be obtained using the ``:browse`` command in an Idris REPL or the corresponding feature in one of the graphical IDE clients to explore the ``Language.Reflection.Elab.Tactics`` namespace.
202All of the built-in tactics contain documentation strings.