[Agda] Small-scale reflection in Agda
Jean-Philippe Bernardy
bernardy at chalmers.se
Fri May 6 15:39:43 CEST 2011
-- In this file I show on examples how to implement Coq-style
-- small-scale reflection in Agda.
-- The Coq implementation of theses examples was explained to me by
-- Anders Mörtberg, and the suggestion to work with the Dec type is
-- due to Stevan Andjelkovic.
-- Case study. I prove two simple theorems using three different
-- methods:
-- 1. "Direct" encoding of propositions in Set.
-- 2. An encoding of small-scale reflection closely related to the
-- one in Coq.
-- 3. Small-scale reflection using the 'Dec' type, as suggested by
-- Stevan.
-- The two theorems that we prove are:
-- 1. ∀ a b -> (a ≤ b) -> suc a ≤ suc b.
-- This problem shows the power of reflection: using it, the
-- conclusion of the theorem computes to ⊤ and it therefore trivial to
-- prove.
-- 2. (a b c : ℕ) -> dividesP a b -> dividesP a c -> dividesP a (b + c)
-- In the proof of the above, reflection is an hindrance. The problem
-- is used to measure the overhead of reflection.
-- Conclusions:
-- 1. The benefits of small-scale reflection can be reaped in Agda as
-- well, but the syntactic overhead might be justified only for
-- specific problems.
-- 2. As Stevan suspected, it seems nicer to depart slightly
-- from the Coq implementation and use the notion of decidable
-- predicate as defined in the standard library.
-------------------------------------------------------
module SSReflect where
open import Data.Empty
open import Data.Unit hiding (_≟_; _≤?_; _≤_)
open import Data.Product
open import Data.Bool hiding (_≟_)
open import Data.Nat
open import Data.Nat.Properties
open import Relation.Nullary
open import Relation.Binary.PropositionalEquality
open import Relation.Nullary.Decidable
open import Relation.Nullary.Product
open import Relation.Nullary.Sum
import Data.Fin as F
import Data.List as L
-- The divisibility predicate.
dividesP : ℕ -> ℕ -> Set
dividesP m n = Σ _ (\(x : ℕ) -> x * m ≡ n)
-- Helper theorem
postulate mulr-alddl : ∀ x y a -> (x + y) * a ≡ x * a + y * a
module 1:Direct where
divides-add : (a b c : ℕ) -> dividesP a b -> dividesP a c ->
dividesP a (b + c)
divides-add a b c (x , bax) (y , bay) = (x + y) , trans (mulr-alddl
x y a) (cong₂ _+_ bax bay)
-- The proof offers no surprise.
suc-lt : ∀ a b -> (a ≤ b) -> suc a ≤ suc b
suc-lt a b p = s≤s p -- Note that, despite the proof being easy, we
-- still need to construct something.
module 2:UsingReflect where
-- The reflection predicate
data Ref (P : Set) : Bool -> Set where
true : P -> Ref P true
false : (P -> ⊥) -> Ref P false
-- Helper to convert a witness of a proposition "P" to the
-- corresponding boolean expression.
unRef : ∀ {P b} -> Ref P b -> P -> T b
unRef (true y) p = tt
unRef (false y) p = y p
postulate
-- for divisibility we need the divisibility predicate as a boolean
divides : ℕ -> ℕ -> Bool
-- ... and show that it properly reflects dividesP.
reflect : ∀ m n -> Ref (dividesP m n) (divides m n)
-- From there we can go in the direction 'Predicate ~> Boolean is
true' easily:
dividesR : ∀ {m n} -> dividesP m n -> T (divides m n)
dividesR {m} {n} = unRef (reflect m n)
-- Note that "T" corresponds to the coercion in Coq.
divides-add : (a b c : ℕ) -> T (divides a b) -> T (divides a c)
-> T (divides a (b + c))
divides-add a b c p q with divides a b | reflect a b | divides a c |
reflect a c
-- we need to do a double "with" to satisfy the unifier.
divides-add a b c p q | .true | true (x , bax) | .true | true (y , bay) =
dividesR (((x + y) , trans (mulr-alddl x y a) (cong₂ _+_ bax bay)))
divides-add a b c () q | .false | false y | .true | true y'
-- Overhead: we need to write something for the "false" cases.
divides-add a b c p () | t | w | .false | false y
_<=_ : ℕ -> ℕ -> Bool
zero <= n = true
suc n <= zero = false
suc n <= suc n' = n <= n'
suc-lt : ∀ a b -> T (a <= b) -> T (suc a <= suc b)
suc-lt a b p with a <= b
suc-lt a b p | true = tt -- Note that the result is computed, we
-- need not do anything here. (As I
-- understand it, this is the big advantage
-- of small-scale reflection)
suc-lt a b () | false
module 3:UsingDec where
-- Here we can re-use Dec from the standard library, and all the
-- goodies coming with it.
-- We need only one function that does the job of 'divides' and
-- 'reflect' at once:
postulate dividesD : (m n : ℕ) -> Dec (dividesP m n)
-- Note that "True" corresponds to the coercion in Coq.
divides-add : (a b c : ℕ) -> True (dividesD a b) -> True (dividesD
a c) -> True (dividesD a (b + c))
divides-add a b c p q with dividesD a b | dividesD a c
-- a single 'with' is sufficient here.
divides-add a b c p q | yes (x , bax) | yes (y , bay)
= fromWitness ((x + y) , trans (mulr-alddl x y a) (cong₂ _+_ bax bay))
divides-add a b c p () | s | no ¬p
divides-add a b c () q | no ¬p | s
suc-lt : ∀ a b -> True (a ≤? b) -> True (suc a ≤? suc b)
suc-lt a b q with a ≤? b
suc-lt a b _ | yes _ = tt -- computed!
suc-lt a b () | no _
-- Note: we can sometimes delegate the false-cases to the following
-- helper function:
dec : ∀ {A : Set} -> ∀ {P : Set} -> {d : Dec P} -> True d -> (P -> A) -> A
dec {A} {P} {yes p} _ k = k p
dec {A} {P} {no _} () k
divides-add' : (a b c : ℕ) -> True (dividesD a b) -> True
(dividesD a c) -> True (dividesD a (b + c))
divides-add' a b c p q = dec p (λ xx → dec q (λ yy → fromWitness
(1:Direct.divides-add a b c xx yy)))
More information about the Agda
mailing list