Some inputs:
module error where
open import Data.Nat as ℕ
open import Level
open import Data.Vec
open import Data.Vec.N-ary
This function constructs a function type from vector of types and result type:
N-Ary-from-Vec : {α γ : Level} {l : ℕ} -> Vec (Set α) l -> Set γ -> Set (N-ary-level α γ l)
N-Ary-from-Vec [] Z = Z
N-Ary-from-Vec (X ∷ Xs) Z = X -> N-Ary-from-Vec Xs Z
This is two combinators from the Arity-Generic Datatype-Generic Programming paper:
v∀⇒ : {n : ℕ} {α β : Level} {A : Set α}
-> (Vec A n -> Set β)
-> Set (N-ary-level α β n)
v∀⇒ {0} B = B []
v∀⇒ {ℕ.suc n} B = ∀ {x} -> v∀⇒ (λ xs -> B (x ∷ xs))
vλ⇒ : {n : ℕ} {α β : Level} {A : Set α} {B : Vec A n -> Set β}
-> ((xs : Vec A n) -> B xs)
-> v∀⇒ B
vλ⇒ {0} f = f []
vλ⇒ {ℕ.suc n} f = λ {x} -> vλ⇒ (λ xs -> f (x ∷ xs))
Some valid definitions:
ok₁ : {α γ : Level} {Z : Set γ} {l : ℕ}
-> (Xs : Vec (Set α) l)
-> N-Ary-from-Vec Xs Z
-> N-Ary-from-Vec Xs Z
ok₁ = {!!}
ok₁' : {α γ : Level} {Z : Set γ}
-> (l : ℕ)
-> v∀⇒ (λ (Xs : Vec (Set α) l)
-> N-Ary-from-Vec Xs Z
-> N-Ary-from-Vec Xs Z)
ok₁' l = vλ⇒ {l} ok₁
ok₂ : {α γ : Level} {Z : Set γ} {l : ℕ}
-> (Xs : Vec (Set α) l)
-> N-Ary-from-Vec Xs (N-Ary-from-Vec Xs Z)
ok₂ = {!!}
ok₂' : {α γ : Level} {Z : Set γ}
-> (l : ℕ)
-> v∀⇒ (λ (Xs : Vec (Set α) l)
-> N-Ary-from-Vec Xs (N-Ary-from-Vec Xs Z))
ok₂' l = vλ⇒ {l} ok₂
And even:
ok₃ : {α γ : Level} {Z : Set γ} {l : ℕ}
-> (Xs : Vec (Set α) l)
-> N-Ary-from-Vec Xs Z
-> N-Ary-from-Vec Xs (N-Ary-from-Vec Xs Z)
ok₃ = {!!}
ok₃' : {α γ : Level} {Z : Set γ}
-> (l : ℕ)
-> {x1 x2 x3 : Set α}
-> N-Ary-from-Vec (x1 ∷ x2 ∷ x3 ∷ []) Z
-> N-Ary-from-Vec (x1 ∷ x2 ∷ x3 ∷ []) (N-Ary-from-Vec (x1 ∷ x2 ∷ x3 ∷ []) Z)
ok₃' l = vλ⇒ {3} ok₃
But this doesn't typecheck:
error' : {α γ : Level} {Z : Set γ}
-> (l : ℕ)
-> v∀⇒ (λ (Xs : Vec (Set α) l)
-> N-Ary-from-Vec Xs Z
-> N-Ary-from-Vec Xs (N-Ary-from-Vec Xs Z))
error' l = vλ⇒ {l} ok₃
Error:
N-ary-level .α (_γ_183 l) l != .γ of type Level
when checking that the expression vλ⇒ {l} ok₃ has type
v∀⇒
(λ Xs →
N-Ary-from-Vec Xs .Z → N-Ary-from-Vec Xs (N-Ary-from-Vec Xs .Z))
What's the problem?
There's no problem, the code you wrote is actually valid. Looks like older versions of Agda have trouble accepting it (development version from November certainly does), but it works fine in current development version.
It seems that unifications can't quite figure out what goes where, so if you are willing to help it a bit, you can get it to type check even in the older versions:
error' : {α γ : Level} {Z : Set γ}
-> (l : ℕ)
-> v∀⇒ (λ (Xs : Vec (Set α) l)
-> N-Ary-from-Vec Xs Z
-> N-Ary-from-Vec Xs (N-Ary-from-Vec Xs Z))
error' {_} {γ} l = vλ⇒ {l} (ok₃ {_} {γ})
Related
I'm trying to prove that a simple FSA in Agda only accepts string which end in zero- this is the first example in Sipser's book. I didn't implement evalFSA as a predicate, but rather as function, and am confused as to whether this was the right or wrong choice, as I'm now having trouble proving the soundness and completeness results with respect to the machine and the language, and whether this implementation detail is the cause of my difficululties.
As soon as I pattern match on x below, it highlights the below line blue. what does this mean, why is it doing it, and why does pattern matching on x0 resolve it?
soundM : (xs : List Σ') → evalFSA' M xs → endsIn0 xs
soundM (x ∷ []) evM = {!!}
soundM (x0 ∷ x1 ∷ xs) evM = {!!}
-- soundM (0' ∷ []) f = tt
and here is the final issue. why can't I apply the recursive call in the 1' case? the only difference between the f's is the use current state of the machine and the input string, but obviously this seems like a symmetry of the system that shouldn't effect our ability to compute.
soundM' : (xs : List Σ') → evalFSA' M xs → endsIn0 xs
soundM' (0' ∷ []) evM = tt
soundM' (0' ∷ x1 ∷ xs) f = soundM' (x1 ∷ xs) f
soundM' (1' ∷ x1 ∷ xs) f = soundM' {!!} f
Here is the inferred f in the 0' case:
f : modal.helper M 0' (x1 ∷ xs) M xs (δ' S₁ x1)
And similairly in the 1' case:
f : modal.helper M 1' (x1 ∷ xs) M xs (δ' S₂ x1)
I'm having, simultaneous issues with what I'm calling completeness as well
completeM : (xs : List Σ') → endsIn0 xs → evalFSA' M xs ≡ ⊤
completeM (0' ∷ []) ex = refl
completeM (0' ∷ x1 ∷ xs) ex = completeM (x1 ∷ xs) ex
completeM (1' ∷ x1 ∷ xs) ex = {!!}
Here is the code to get here
module fsa where
open import Bool
open import Level using (_⊔_)
open import Data.Nat.Base as Nat using (ℕ; zero; suc; _<′_; _+_)
open import Data.List.Base as List using (List; []; _∷_)
-- open import Data.Product as Prod using (∃; _×_; _,_)
open import Data.Empty
open import Data.Unit
open import Relation.Binary.PropositionalEquality using (_≡_; refl; subst)
-- open import Data.Fin as Fin
record FSA : Set₁ where
field
Q : Set
Σ : Set
δ : Q → Σ → Q
q₀ : Q
F : Q → Set
evalFSA' : (fsa : FSA) → List (FSA.Σ fsa) → Set
evalFSA' fsa [] = ⊥
evalFSA' fsa (x ∷ xs) = helper fsa (x ∷ xs) (FSA.q₀ fsa)
where
helper : (fsa : FSA) → List (FSA.Σ fsa) → (FSA.Q fsa) → Set
helper fsa [] q = FSA.F fsa q
helper fsa (x ∷ xs) q = helper fsa xs ((FSA.δ fsa) q x)
data Q' : Set where
S₁ : Q'
S₂ : Q'
data Σ' : Set where
0' : Σ'
1' : Σ'
q₀' : Q'
q₀' = S₁
F' : Q' → Set
F' S₁ = ⊤
F' S₂ = ⊥
δ' : Q' → Σ' → Q'
δ' S₁ 0' = S₁
δ' S₁ 1' = S₂
δ' S₂ 0' = S₁
δ' S₂ 1' = S₂
M : FSA
FSA.Q M = Q'
FSA.Σ M = Σ'
FSA.δ M = δ'
FSA.q₀ M = q₀'
FSA.F M = F'
exF1 = evalFSA' M (0' ∷ [])
exF2 = evalFSA' M (1' ∷ (0' ∷ 0' ∷ 1' ∷ []))
-- a more general endIn that i was orignally trying to use, but likewise failed to get to work
data Dec (A : Set) : Set where
yes : A → Dec A
no : (A → ⊥) → Dec A
sigDec : (x y : Σ') → Dec (x ≡ y)
sigDec 0' 0' = yes refl
sigDec 0' 1' = no (λ ())
sigDec 1' 0' = no (λ ())
sigDec 1' 1' = yes refl
endsIn : {X : Set} → ((x y : X) → Dec (x ≡ y)) → List X → X → Set
endsIn d [] x = ⊥
endsIn d (x ∷ []) x0 with (d x0 x)
... | yes refl = ⊤
... | no x1 = ⊥
endsIn d (x ∷ x1 ∷ xs) x0 = endsIn d (x1 ∷ xs) x0
_endsIn'_ : List Σ' → Σ' → Set
xs endsIn' x = endsIn sigDec xs x
endsIn0 : List Σ' → Set
endsIn0 [] = ⊥
endsIn0 (0' ∷ []) = ⊤
endsIn0 (0' ∷ x ∷ xs) = endsIn0 (x ∷ xs)
endsIn0 (1' ∷ xs) = endsIn0 xs
-- testing
10endsin0 = (1' ∷ 0' ∷ []) endsIn' 0'
n10endsin0 = (1' ∷ 1' ∷ []) endsIn' 0'
Your post is very large and contains numerous elements, which all can be commented in different ways. I will address them one by one, and explain the choices I made to make these elements more easily accessible. Note that these choices consist in minor elements in your codes, mostly cosmetic, which do not change in any way the semantics of your definitions. I start by giving you the correct code in details, after which I answer the questions.
The correct code in details
Let us start by cleaning up these imports to the minimum needed:
module FSA where
open import Data.List.Base
open import Data.Empty
open import Data.Unit
I then kept your definition of your automaton record:
record FSA : Set₁ where
field
Q : Set
Σ : Set
δ : Q → Σ → Q
q₀ : Q
F : Q → Set
I have extracted your helper function from the evalFSA' function. The reason for this change is that when using when, the function inherits all the parameters from the parent function, which makes it harder to comprehend further goals such as modal.helper M 0' (x1 ∷ xs) M xs (δ' S₁ x1).
helper : (fsa : FSA) → List (FSA.Σ fsa) → (FSA.Q fsa) → Set
helper fsa [] q = FSA.F fsa q
helper fsa (x ∷ xs) q = helper fsa xs ((FSA.δ fsa) q x)
evalFSA' : (fsa : FSA) → List (FSA.Σ fsa) → Set
evalFSA' fsa [] = ⊥
evalFSA' fsa (x ∷ xs) = helper fsa (x ∷ xs) (FSA.q₀ fsa)
Then your case study automaton remains the same, although I simplified the record instantiation without using copatterns:
data Q' : Set where
S₁ : Q'
S₂ : Q'
data Σ' : Set where
0' : Σ'
1' : Σ'
q₀' : Q'
q₀' = S₁
F' : Q' → Set
F' S₁ = ⊤
F' S₂ = ⊥
δ' : Q' → Σ' → Q'
δ' S₁ 0' = S₁
δ' S₁ 1' = S₂
δ' S₂ 0' = S₁
δ' S₂ 1' = S₂
M : FSA
M = record { Q = Q' ; Σ = Σ' ; δ = δ' ; q₀ = q₀' ; F = F' }
I also simplified your predicate endsWith0 as follows:
endsWith0 : List Σ' → Set
endsWith0 [] = ⊥
endsWith0 (0' ∷ []) = ⊤
endsWith0 (_ ∷ xs) = endsWith0 xs
From this point on, soundM and completeM are proved as follows (I homogenized their signatures):
soundM : ∀ xs → evalFSA' M xs → endsWith0 xs
soundM (0' ∷ []) evM = evM
soundM (0' ∷ x₁ ∷ xs) evM = soundM (x₁ ∷ xs) evM
soundM (1' ∷ 0' ∷ xs) evM = soundM (0' ∷ xs) evM
soundM (1' ∷ 1' ∷ xs) evM = soundM (1' ∷ xs) evM
completeM : ∀ xs → endsWith0 xs → evalFSA' M xs
completeM (0' ∷ []) ex = ex
completeM (0' ∷ x₁ ∷ xs) = completeM (x₁ ∷ xs)
completeM (1' ∷ 0' ∷ xs) = completeM (0' ∷ xs)
completeM (1' ∷ 1' ∷ xs) = completeM (1' ∷ xs)
Answers to non-proof related questions
On predicates vs functions returning types, you asked:
I didn't implement evalFSA as a predicate, but rather as function,
and am confused as to whether this was the right or wrong choice
There are no good answer to this question. Both ideas are possible, and often debated on other questions on this site. I personally always use predicates when possible, but others have arguments in favor of functions returning ⊤ or ⊥. And, as you noticed it is possible to prove what you wanted using your implementation.
On the weird highlighting, you asked:
it highlights the below line blue. what does this mean
As far as I know, this is a bug. Some weird coloration like that started to occasionally happen to me recently as well, but they apparently do not mean anything.
Answer to your proof-related question
You asked the following question:
why can't I apply the recursive call in the 1' case? the only
difference between the f's is the use current state of the machine and
the input string, but obviously this seems like a symmetry of the
system that shouldn't effect our ability to compute
Answering this question is actually quite simple, but this answer was somewhat hidden in your implementation because you embedded the definition of helper inside the definition of evalFSA', which I changed as explained.
Let us consider the following code while proving soundM:
soundM : (xs : List Σ') → evalFSA' M xs → endsWith0 xs
soundM (0' ∷ []) evM = evM
soundM (0' ∷ x ∷ xs) evM = soundM (x ∷ xs) {!evM!}
soundM (1' ∷ x ∷ xs) evM = soundM (x ∷ xs) {!evM!}
When asking Agda what is the goal and the type of the current element in the first goal, it answers:
Goal: helper M xs (δ' q₀' x)
Have: helper M xs (δ' S₁ x)
Since you have defined q₀' as follows:
q₀' : Q'
q₀' = S₁
Agda know that q₀' is definitionally equal to S₁ which means the current term has actually the same type as the goal, which is why it is accepted.
In the other hole however, asking Agda the same information gives:
Goal: helper M xs (δ' q₀' x)
Have: helper M xs (δ' S₂ x)
In this case, q₀' is not definitionally equal to S₂ (and not equal in any way actually) which means these types are not equal, and it is not possible to conclude right away.
As shown in my code, pattern matching an additional time on x allows agda to further reduce the goal which allows us to conclude.
A similar reasoning is used to provide a proof of completeM
I'm trying to port msubst_R from Software Foundations, vol. 2 to Agda. I'm trying to avoid a lot of busywork by using a typed representation for terms. Below is my port of everything up to msubst_R; I think everything is fine below but it's needed for the problematic part.
open import Data.Nat
open import Relation.Binary.PropositionalEquality hiding (subst)
open import Data.Empty
open import Data.Unit
open import Relation.Binary
open import Data.Star
open import Level renaming (zero to lzero)
open import Data.Product
open import Function.Equivalence hiding (sym)
open import Function.Equality using (_⟨$⟩_)
data Ty : Set where
fun : Ty → Ty → Ty
infixl 21 _▷_
data Ctx : Set where
[] : Ctx
_▷_ : Ctx → Ty → Ctx
data Var (t : Ty) : Ctx → Set where
vz : ∀ {Γ} → Var t (Γ ▷ t)
vs : ∀ {Γ u} → Var t Γ → Var t (Γ ▷ u)
data _⊆_ : Ctx → Ctx → Set where
done : ∀ {Δ} → [] ⊆ Δ
keep : ∀ {Γ Δ a} → Γ ⊆ Δ → Γ ▷ a ⊆ Δ ▷ a
drop : ∀ {Γ Δ a} → Γ ⊆ Δ → Γ ⊆ Δ ▷ a
⊆-refl : ∀ {Γ} → Γ ⊆ Γ
⊆-refl {[]} = done
⊆-refl {Γ ▷ _} = keep ⊆-refl
data Tm (Γ : Ctx) : Ty → Set where
var : ∀ {t} → Var t Γ → Tm Γ t
lam : ∀ t {u} → (e : Tm (Γ ▷ t) u) → Tm Γ (fun t u)
app : ∀ {u t} → (f : Tm Γ (fun u t)) → (e : Tm Γ u) → Tm Γ t
wk-var : ∀ {Γ Δ t} → Γ ⊆ Δ → Var t Γ → Var t Δ
wk-var done ()
wk-var (keep Γ⊆Δ) vz = vz
wk-var (keep Γ⊆Δ) (vs v) = vs (wk-var Γ⊆Δ v)
wk-var (drop Γ⊆Δ) v = vs (wk-var Γ⊆Δ v)
wk : ∀ {Γ Δ t} → Γ ⊆ Δ → Tm Γ t → Tm Δ t
wk Γ⊆Δ (var v) = var (wk-var Γ⊆Δ v)
wk Γ⊆Δ (lam t e) = lam t (wk (keep Γ⊆Δ) e)
wk Γ⊆Δ (app f e) = app (wk Γ⊆Δ f) (wk Γ⊆Δ e)
data _⊢⋆_ (Γ : Ctx) : Ctx → Set where
[] : Γ ⊢⋆ []
_▷_ : ∀ {Δ t} → Γ ⊢⋆ Δ → Tm Γ t → Γ ⊢⋆ Δ ▷ t
⊢⋆-wk : ∀ {Γ Δ} t → Γ ⊢⋆ Δ → Γ ▷ t ⊢⋆ Δ
⊢⋆-wk t [] = []
⊢⋆-wk t (σ ▷ e) = (⊢⋆-wk t σ) ▷ wk (drop ⊆-refl) e
⊢⋆-mono : ∀ {Γ Δ t} → Γ ⊢⋆ Δ → Γ ▷ t ⊢⋆ Δ ▷ t
⊢⋆-mono σ = ⊢⋆-wk _ σ ▷ var vz
⊢⋆-refl : ∀ {Γ} → Γ ⊢⋆ Γ
⊢⋆-refl {[]} = []
⊢⋆-refl {Γ ▷ _} = ⊢⋆-mono ⊢⋆-refl
subst-var : ∀ {Γ Δ t} → Γ ⊢⋆ Δ → Var t Δ → Tm Γ t
subst-var [] ()
subst-var (σ ▷ x) vz = x
subst-var (σ ▷ x) (vs v) = subst-var σ v
subst : ∀ {Γ Δ t} → Γ ⊢⋆ Δ → Tm Δ t → Tm Γ t
subst σ (var x) = subst-var σ x
subst σ (lam t e) = lam t (subst (⊢⋆-mono σ) e)
subst σ (app f e) = app (subst σ f) (subst σ e)
data Value : {Γ : Ctx} → {t : Ty} → Tm Γ t → Set where
lam : ∀ {Γ t} → ∀ u (e : Tm _ t) → Value {Γ} (lam u e)
data _==>_ {Γ} : ∀ {t} → Rel (Tm Γ t) lzero where
app-lam : ∀ {t u} (f : Tm _ t) {v : Tm _ u} → Value v → app (lam u f) v ==> subst (⊢⋆-refl ▷ v) f
appˡ : ∀ {t u} {f f′ : Tm Γ (fun u t)} → f ==> f′ → (e : Tm Γ u) → app f e ==> app f′ e
appʳ : ∀ {t u} {f} → Value {Γ} {fun u t} f → ∀ {e e′ : Tm Γ u} → e ==> e′ → app f e ==> app f e′
_==>*_ : ∀ {Γ t} → Rel (Tm Γ t) _
_==>*_ = Star _==>_
NF : ∀ {a b} {A : Set a} → Rel A b → A → Set _
NF step x = ∄ (step x)
value⇒normal : ∀ {Γ t e} → Value {Γ} {t} e → NF _==>_ e
value⇒normal (lam t e) (_ , ())
Deterministic : ∀ {a b} {A : Set a} → Rel A b → Set _
Deterministic step = ∀ {x y y′} → step x y → step x y′ → y ≡ y′
deterministic : ∀ {Γ t} → Deterministic (_==>_ {Γ} {t})
deterministic (app-lam f _) (app-lam ._ _) = refl
deterministic (app-lam f v) (appˡ () _)
deterministic (app-lam f v) (appʳ f′ e) = ⊥-elim (value⇒normal v (, e))
deterministic (appˡ () e) (app-lam f v)
deterministic (appˡ f e) (appˡ f′ ._) = cong _ (deterministic f f′)
deterministic (appˡ f e) (appʳ f′ _) = ⊥-elim (value⇒normal f′ (, f))
deterministic (appʳ f e) (app-lam f′ v) = ⊥-elim (value⇒normal v (, e))
deterministic (appʳ f e) (appˡ f′ _) = ⊥-elim (value⇒normal f (, f′))
deterministic (appʳ f e) (appʳ f′ e′) = cong _ (deterministic e e′)
Halts : ∀ {Γ t} → Tm Γ t → Set
Halts e = ∃ λ e′ → e ==>* e′ × Value e′
value⇒halts : ∀ {Γ t e} → Value {Γ} {t} e → Halts e
value⇒halts {e = e} v = e , ε , v
-- -- This would not be strictly positive!
-- data Saturated : ∀ {Γ t} → Tm Γ t → Set where
-- fun : ∀ {t u} {f : Tm [] (fun t u)} → Halts f → (∀ {e} → Saturated e → Saturated (app f e)) → Saturated f
mutual
Saturated : ∀ {t} → Tm [] t → Set
Saturated e = Halts e × Saturated′ _ e
Saturated′ : ∀ t → Tm [] t → Set
Saturated′ (fun t u) f = ∀ {e} → Saturated e → Saturated (app f e)
saturated⇒halts : ∀ {t e} → Saturated {t} e → Halts e
saturated⇒halts = proj₁
step‿preserves‿halting : ∀ {Γ t} {e e′ : Tm Γ t} → e ==> e′ → Halts e ⇔ Halts e′
step‿preserves‿halting {e = e} {e′ = e′} step = equivalence fwd bwd
where
fwd : Halts e → Halts e′
fwd (e″ , ε , v) = ⊥-elim (value⇒normal v (, step))
fwd (e″ , s ◅ steps , v) rewrite deterministic step s = e″ , steps , v
bwd : Halts e′ → Halts e
bwd (e″ , steps , v) = e″ , step ◅ steps , v
step‿preserves‿saturated : ∀ {t} {e e′ : Tm _ t} → e ==> e′ → Saturated e ⇔ Saturated e′
step‿preserves‿saturated step = equivalence (fwd step) (bwd step)
where
fwd : ∀ {t} {e e′ : Tm _ t} → e ==> e′ → Saturated e → Saturated e′
fwd {fun s t} step (halts , sat) = Equivalence.to (step‿preserves‿halting step) ⟨$⟩ halts , λ e → fwd (appˡ step _) (sat e)
bwd : ∀ {t} {e e′ : Tm _ t} → e ==> e′ → Saturated e′ → Saturated e
bwd {fun s t} step (halts , sat) = Equivalence.from (step‿preserves‿halting step) ⟨$⟩ halts , λ e → bwd (appˡ step _) (sat e)
step*‿preserves‿saturated : ∀ {t} {e e′ : Tm _ t} → e ==>* e′ → Saturated e ⇔ Saturated e′
step*‿preserves‿saturated ε = id
step*‿preserves‿saturated (step ◅ steps) = step*‿preserves‿saturated steps ∘ step‿preserves‿saturated step
Note that I have removed the bool and pair types since they are not necessary to show my problem.
The problem, then, is with msubst_R (which I call saturate below):
data Instantiation : ∀ {Γ} → [] ⊢⋆ Γ → Set where
[] : Instantiation []
_▷_ : ∀ {Γ t σ} → Instantiation {Γ} σ → ∀ {e} → Value {_} {t} e × Saturated e → Instantiation (σ ▷ e)
saturate-var : ∀ {Γ σ} → Instantiation σ → ∀ {t} (x : Var t Γ) → Saturated (subst-var σ x)
saturate-var (_ ▷ (_ , sat)) vz = sat
saturate-var (env ▷ _) (vs x) = saturate-var env x
app-lam* : ∀ {Γ t} {e e′ : Tm Γ t} → e ==>* e′ → Value e′ → ∀ {u} (f : Tm _ u) → app (lam t f) e ==>* subst (⊢⋆-refl ▷ e′) f
app-lam* steps v f = gmap _ (appʳ (lam _ _)) steps ◅◅ app-lam f v ◅ ε
saturate : ∀ {Γ σ} → Instantiation σ → ∀ {t} → (e : Tm Γ t) → Saturated (subst σ e)
saturate env (var x) = saturate-var env x
saturate env (lam u f) = value⇒halts (lam u _) , sat-f
where
f′ = subst _ f
sat-f : ∀ {e : Tm _ u} → Saturated e → Saturated (app (lam u f′) e)
sat-f sat#((e′ , steps , v) , _) =
Equivalence.from (step*‿preserves‿saturated (app-lam* steps v f′)) ⟨$⟩ saturate ([] ▷ (v , Equivalence.to (step*‿preserves‿saturated steps) ⟨$⟩ sat)) f′
saturate env (app f e) with saturate env f | saturate env e
saturate env (app f e) | _ , sat-f | sat-e = sat-f sat-e
saturate doesn't pass the termination checker, because in the lam case, sat-f recurses into saturate on f′, which isn't necessarily smaller than lam u f; and [] ▷ e′ is also not necessarily smaller than σ.
Another way of looking at why saturate doesn't terminate is to look at saturate env (app f e). Here, recursing into f and (potentially) e will grow t, even though all the other cases either leave t the same and shrink the term, or shrink t. So if saturate env (app f e) didn't recurse into saturate env f and saturate env e, the recursion in saturate env (lam u f) would not be problematic in itself.
However, I think my code does the right thing for the app f e case (since that's the whole point of lugging around the parametric saturation proof for function types), so it should be the lam u f case where I need a tricky way in which f′ is smaller than lam u f.
What am I missing?
Assuming an additional Bool base type, Saturated would look nicer the following way, since it would not demand a Halts for the fun argument which already follows from Saturated.
Saturated : ∀ {A} → Tm [] A → Set
Saturated {fun A B} t = Halts t × (∀ {u} → Saturated u → Saturated (app t u))
Saturated {Bool} t = Halts t
Then, in saturate you can only recurse on f in the lam case. There is no other way to make it structural. The job is to massage the hypothesis from f into the right shape using the reduction/saturation lemmas.
open import Function using (case_of_)
saturate : ∀ {Γ σ} → Instantiation σ → ∀ {t} → (e : Tm Γ t) → Saturated (subst σ e)
saturate env (var x) = saturate-var env x
saturate env (lam u f) =
value⇒halts (lam _ (subst _ f)) ,
λ {u} usat →
case (saturated⇒halts usat) of λ {(u' , u==>*u' , u'val) →
let hyp = saturate (env ▷ (u'val , Equivalence.to (step*‿preserves‿saturated u==>*u') ⟨$⟩ usat)) f
in {!!}} -- fill this with grunt work
saturate env (app f e) with saturate env f | saturate env e
saturate env (app f e) | _ , sat-f | sat-e = sat-f sat-e
This is an exercise from the Homotopy Type Theory book. Here's what I have:
data ℕ : Set where
zero : ℕ
succ : ℕ → ℕ
iter : {C : Set} → C → (C → C) → ℕ → C
iter z f zero = z
iter z f (succ n) = f (iter z f n)
succℕ : {C : Set} → (ℕ → C → C) → ℕ × C → ℕ × C
succℕ f (n , x) = (succ n , f n x)
iterℕ : {C : Set} → C → (ℕ → C → C) → ℕ → ℕ × C
iterℕ z f = iter (zero , z) (succℕ f)
recℕ : {C : Set} → C → (ℕ → C → C) → ℕ → C
recℕ z f = _×_.proj₂ ∘ iterℕ z f
indℕ : {C : ℕ → Set} → C zero → ((n : ℕ) → C n → C (succ n)) → (n : ℕ) → C n
indℕ z f zero = z
indℕ z f (succ n) = f n (indℕ z f n)
recℕzfzero≡z : {C : Set} {z : C} {f : ℕ → C → C} → recℕ z f zero ≡ z
recℕzfzero≡z = refl
recℕzfsuccn≡fnrecℕzfn : {C : Set} {z : C} {f : ℕ → C → C} (n : ℕ) →
recℕ z f (succ n) ≡ f n (recℕ z f n)
recℕzfsuccn≡fnrecℕzfn = indℕ refl ?
I don't know how to prove that recℕ z f (succ n) ≡ f n (recℕ z f n). I need to prove:
(n : ℕ) → recℕ z f (succ n) ≡ f n (recℕ z f n)
→ recℕ z f (succ (succ n)) ≡ f (succ n) (recℕ z f (succ n))
In English, given a natural number n and the induction hypothesis prove the consequent.
The infix operator _∘_ is normal function composition. The _≡_ and _×_ data types are defined as:
data _≡_ {A : Set} : A → A → Set where
refl : {x : A} → x ≡ x
record _×_ (A B : Set) : Set where
constructor _,_
field
_×_.proj₁ : A
_×_.proj₂ : B
I've been thinking of a solution for quite a while but I can't figure out how to solve this problem.
Let's get some help from Agda-mode for emacs:
recℕzfsuccn≡fnrecℕzfn : {C : Set} {z : C} {f : ℕ → C → C} (n : ℕ) →
recℕ z f (succ n) ≡ f n (recℕ z f n)
recℕzfsuccn≡fnrecℕzfn {f = f} n = {!!}
If we normalize the context in the hole by typing C-u C-u C-c C-, (each time I feel like I'm trying to invoke fatality in Mortal Kombat), we'll see (I cleaned up your definitions a bit)
Goal: f
(proj₁
(iter (0 , .z) (λ nx → succ (proj₁ nx) , f (proj₁ nx) (proj₂ nx))
n))
(proj₂
(iter (0 , .z) (λ nx → succ (proj₁ nx) , f (proj₁ nx) (proj₂ nx))
n))
≡
f n
(proj₂
(iter (0 , .z) (λ nx → succ (proj₁ nx) , f (proj₁ nx) (proj₂ nx))
n))
The second arguments to f are equal at both sides, so we can write
recℕzfsuccn≡fnrecℕzfn {f = f} n = cong (λ m -> f m (recℕ _ f n)) {!!}
where
cong : ∀ {a b} {A : Set a} {B : Set b}
(f : A → B) {x y} → x ≡ y → f x ≡ f y
cong f refl = refl
Now the goal is
Goal: proj₁ (iter (zero , .z) (succℕ f) n) ≡ n
which is a straightforward lemma
lem : {C : Set} {z : C} {f : ℕ → C → C} (n : ℕ)
→ proj₁ (iter (zero , z) (succℕ f) n) ≡ n
lem = indℕ refl (λ _ -> cong succ)
So
recℕzfsuccn≡fnrecℕzfn : {C : Set} {z : C} {f : ℕ → C → C} (n : ℕ) →
recℕ z f (succ n) ≡ f n (recℕ z f n)
recℕzfsuccn≡fnrecℕzfn {f = f} n = cong (λ m -> f m (recℕ _ f n)) (lem n)
The whole code.
I want to divide two natural number. I have made function like this
_/_ : N -> N -> frac
m / one = m / one
(suc m) / n = ?? I dont know what to write here.
Please help.
As #gallais says you can use well-founded recursion explicitly, but I don't like this approach, because it's totally unreadable.
This datatype
record Is {α} {A : Set α} (x : A) : Set α where
¡ = x
open Is
! : ∀ {α} {A : Set α} -> (x : A) -> Is x
! _ = _
allows to lift values to the type level, for example you can define a type-safe pred function:
pred⁺ : ∀ {n} -> Is (suc n) -> ℕ
pred⁺ = pred ∘ ¡
Then
test-1 : pred⁺ (! 1) ≡ 0
test-1 = refl
typechecks, while
fail : pred⁺ (! 0) ≡ 0
fail = refl
doesn't. It's possible to define subtraction with positive subtrahend (to ensure well-foundness) in the same way:
_-⁺_ : ∀ {m} -> ℕ -> Is (suc m) -> ℕ
n -⁺ im = n ∸ ¡ im
Then using stuff that I described here, you can repeatedly subtract one number from another until the difference is smaller than the second number:
lem : ∀ {n m} {im : Is (suc m)} -> m < n -> n -⁺ im <′ n
lem {suc n} {m} (s≤s _) = s≤′s (≤⇒≤′ (n∸m≤n m n))
iter-sub : ∀ {m} -> ℕ -> Is (suc m) -> List ℕ
iter-sub n im = calls (λ n -> n -⁺ im) <-well-founded lem (_≤?_ (¡ im)) n
For example
test-1 : iter-sub 10 (! 3) ≡ 10 ∷ 7 ∷ 4 ∷ []
test-1 = refl
test-2 : iter-sub 16 (! 4) ≡ 16 ∷ 12 ∷ 8 ∷ 4 ∷ []
test-2 = refl
div⁺ then is simply
_div⁺_ : ∀ {m} -> ℕ -> Is (suc m) -> ℕ
n div⁺ im = length (iter-sub n im)
And a version similar to the one in the Data.Nat.DivMod module (only without the Mod part):
_div_ : ℕ -> (m : ℕ) {_ : False (m ≟ 0)} -> ℕ
n div 0 = λ{()}
n div (suc m) = n div⁺ (! (suc m))
Some tests:
test-3 : map (λ n -> n div 3)
(0 ∷ 1 ∷ 2 ∷ 3 ∷ 4 ∷ 5 ∷ 6 ∷ 7 ∷ 8 ∷ 9 ∷ [])
≡ (0 ∷ 0 ∷ 0 ∷ 1 ∷ 1 ∷ 1 ∷ 2 ∷ 2 ∷ 2 ∷ 3 ∷ [])
test-3 = refl
Note however, that the version in the standard library also contains the soundness proof:
property : dividend ≡ toℕ remainder + quotient * divisor
The whole code.
Division is usually defined as iterated substraction which requires a slightly unusual induction principle. See e.g. the definition in the standard library.
I'm having difficulty convincing Agda to termination-check the function fmap below and similar functions defined recursively over the structure of a Trie. A Trie is a trie whose domain is a Type, an object-level type formed from unit, products and fixed points (I've omitted coproducts to keep the code minimal). The problem seems to relate to a type-level substitution I use in the definition of Trie. (The expression const (μₜ τ) * τ means apply the substitution const (μₜ τ) to the type τ.)
module Temp where
open import Data.Unit
open import Category.Functor
open import Function
open import Level
open import Relation.Binary
-- A context is just a snoc-list.
data Cxt {𝒂} (A : Set 𝒂) : Set 𝒂 where
ε : Cxt A
_∷ᵣ_ : Cxt A → A → Cxt A
-- Context membership.
data _∈_ {𝒂} {A : Set 𝒂} (a : A) : Cxt A → Set 𝒂 where
here : ∀ {Δ} → a ∈ Δ ∷ᵣ a
there : ∀ {Δ a′} → a ∈ Δ → a ∈ Δ ∷ᵣ a′
infix 3 _∈_
-- Well-formed types, using de Bruijn indices.
data _⊦ (Δ : Cxt ⊤) : Set where
nat : Δ ⊦
𝟏 : Δ ⊦
var : _ ∈ Δ → Δ ⊦
_+_ _⨰_ : Δ ⊦ → Δ ⊦ → Δ ⊦
μ : Δ ∷ᵣ _ ⊦ → Δ ⊦
infix 3 _⊦
-- A closed type.
Type : Set
Type = ε ⊦
-- Type-level substitutions and renamings.
Sub Ren : Rel (Cxt ⊤) zero
Sub Δ Δ′ = _ ∈ Δ → Δ′ ⊦
Ren Δ Δ′ = ∀ {x} → x ∈ Δ → x ∈ Δ′
-- Renaming extension.
extendᵣ : ∀ {Δ Δ′} → Ren Δ Δ′ → Ren (Δ ∷ᵣ _) (Δ′ ∷ᵣ _)
extendᵣ ρ here = here
extendᵣ ρ (there x) = there (ρ x)
-- Lift a type renaming to a type.
_*ᵣ_ : ∀ {Δ Δ′} → Ren Δ Δ′ → Δ ⊦ → Δ′ ⊦
_ *ᵣ nat = nat
_ *ᵣ 𝟏 = 𝟏
ρ *ᵣ (var x) = var (ρ x)
ρ *ᵣ (τ₁ + τ₂) = (ρ *ᵣ τ₁) + (ρ *ᵣ τ₂)
ρ *ᵣ (τ₁ ⨰ τ₂) = (ρ *ᵣ τ₁) ⨰ (ρ *ᵣ τ₂)
ρ *ᵣ (μ τ) = μ (extendᵣ ρ *ᵣ τ)
-- Substitution extension.
extend : ∀ {Δ Δ′} → Sub Δ Δ′ → Sub (Δ ∷ᵣ _) (Δ′ ∷ᵣ _)
extend θ here = var here
extend θ (there x) = there *ᵣ (θ x)
-- Lift a type substitution to a type.
_*_ : ∀ {Δ Δ′} → Sub Δ Δ′ → Δ ⊦ → Δ′ ⊦
θ * nat = nat
θ * 𝟏 = 𝟏
θ * var x = θ x
θ * (τ₁ + τ₂) = (θ * τ₁) + (θ * τ₂)
θ * (τ₁ ⨰ τ₂) = (θ * τ₁) ⨰ (θ * τ₂)
θ * μ τ = μ (extend θ * τ)
data Trie {𝒂} (A : Set 𝒂) : Type → Set 𝒂 where
〈〉 : A → 𝟏 ▷ A
〔_,_〕 : ∀ {τ₁ τ₂} → τ₁ ▷ A → τ₂ ▷ A → τ₁ + τ₂ ▷ A
↑_ : ∀ {τ₁ τ₂} → τ₁ ▷ τ₂ ▷ A → τ₁ ⨰ τ₂ ▷ A
roll : ∀ {τ} → (const (μ τ) * τ) ▷ A → μ τ ▷ A
infixr 5 Trie
syntax Trie A τ = τ ▷ A
{-# NO_TERMINATION_CHECK #-}
fmap : ∀ {a} {A B : Set a} {τ} → (A → B) → τ ▷ A → τ ▷ B
fmap f (〈〉 x) = 〈〉 (f x)
fmap f 〔 σ₁ , σ₂ 〕 = 〔 fmap f σ₁ , fmap f σ₂ 〕
fmap f (↑ σ) = ↑ (fmap (fmap f) σ)
fmap f (roll σ) = roll (fmap f σ)
It would seem that fmap recurses into a strictly smaller argument in each case; certainly the product case is fine if I remove recursive types. On the other hand, the definition handles recursive types fine if I remove products.
What's the simplest way to proceed here? The inline/fuse trick does not look particularly applicable, but maybe it is. Or should I be looking for another way to deal with the substitution in the definition of Trie?
The inline/fuse trick can be applied in (perhaps) surprising way. This trick is suited for problems of this sort:
data Trie (A : Set) : Set where
nil : Trie A
node : A → List (Trie A) → Trie A
map-trie : {A B : Set} → (A → B) → Trie A → Trie B
map-trie f nil = nil
map-trie f (node x xs) = node (f x) (map (map-trie f) xs)
This function is structurally recursive, but in a hidden way. map just applies map-trie f to the elements of xs, so map-trie gets applied to smaller (sub-)tries. But Agda doesn't look through the definition of map to see that it doesn't do anything funky. So we must apply the inline/fuse trick to get it past termination checker:
map-trie : {A B : Set} → (A → B) → Trie A → Trie B
map-trie f nil = nil
map-trie {A} {B} f (node x xs) = node (f x) (map′ xs)
where
map′ : List (Trie A) → List (Trie B)
map′ [] = []
map′ (x ∷ xs) = map-trie f x ∷ map′ xs
Your fmap function shares the same structure, you map a lifted function of some sort. But what to inline? If we follow the example above, we should inline fmap itself. This looks and feels a bit strange, but indeed, it works:
fmap fmap′ : ∀ {a} {A B : Set a} {τ} → (A → B) → τ ▷ A → τ ▷ B
fmap f (〈〉 x) = 〈〉 (f x)
fmap f 〔 σ₁ , σ₂ 〕 = 〔 fmap f σ₁ , fmap f σ₂ 〕
fmap f (↑ σ) = ↑ (fmap (fmap′ f) σ)
fmap f (roll σ) = roll (fmap f σ)
fmap′ f (〈〉 x) = 〈〉 (f x)
fmap′ f 〔 σ₁ , σ₂ 〕 = 〔 fmap′ f σ₁ , fmap′ f σ₂ 〕
fmap′ f (↑ σ) = ↑ (fmap′ (fmap f) σ)
fmap′ f (roll σ) = roll (fmap′ f σ)
There's another technique you can apply: it's called sized types. Instead of relying on the compiler to figure out when somethig is or is not structurally recursive, you instead specify it directly. However, you have to index your data types by a Size type, so this approach is fairly intrusive and cannot be applied to already existing types, but I think it is worth mentioning.
In its simplest form, sized type behaves as a type indexed by a natural number. This index specifies the upper bound of structural size. You can think of this as an upper bound for the height of a tree (given that the data type is an F-branching tree for some functor F). Sized version of List looks almost like a Vec, for example:
data SizedList (A : Set) : ℕ → Set where
[] : ∀ {n} → SizedList A n
_∷_ : ∀ {n} → A → SizedList A n → SizedList A (suc n)
But sized types add few features that make them easier to use. You have a constant ∞ for the case when you don't care about the size. suc is called ↑ and Agda implements few rules, such as ↑ ∞ = ∞.
Let's rewrite the Trie example to use sized types. We need a pragma at the top of the file and one import:
{-# OPTIONS --sized-types #-}
open import Size
And here's the modified data type:
data Trie (A : Set) : {i : Size} → Set where
nil : ∀ {i} → Trie A {↑ i}
node : ∀ {i} → A → List (Trie A {i}) → Trie A {↑ i}
If you leave the map-trie function as is, the termination checker is still going to complain. That's because when you don't specify any size, Agda will fill in infinity (i.e. don't-care value) and we are back at the beginning.
However, we can mark map-trie as size-preserving:
map-trie : ∀ {i A B} → (A → B) → Trie A {i} → Trie B {i}
map-trie f nil = nil
map-trie f (node x xs) = node (f x) (map (map-trie f) xs)
So, if you give it a Trie bounded by i, it will give you another Trie bounded by i as well. So map-trie can never make the Trie larger, only equally large or smaller. This is enough for the termination checker to figure out that map (map-trie f) xs is okay.
This technique can also be applied to your Trie:
open import Size
renaming (↑_ to ^_)
data Trie {𝒂} (A : Set 𝒂) : {i : Size} → Type → Set 𝒂 where
〈〉 : ∀ {i} → A →
Trie A {^ i} 𝟏
〔_,_〕 : ∀ {i τ₁ τ₂} → Trie A {i} τ₁ → Trie A {i} τ₂ →
Trie A {^ i} (τ₁ + τ₂)
↑_ : ∀ {i τ₁ τ₂} → Trie (Trie A {i} τ₂) {i} τ₁ →
Trie A {^ i} (τ₁ ⨰ τ₂)
roll : ∀ {i τ} → Trie A {i} (const (μ τ) * τ) →
Trie A {^ i} (μ τ)
infixr 5 Trie
syntax Trie A τ = τ ▷ A
fmap : ∀ {i 𝒂} {A B : Set 𝒂} {τ} → (A → B) → Trie A {i} τ → Trie B {i} τ
fmap f (〈〉 x) = 〈〉 (f x)
fmap f 〔 σ₁ , σ₂ 〕 = 〔 fmap f σ₁ , fmap f σ₂ 〕
fmap f (↑ σ) = ↑ fmap (fmap f) σ
fmap f (roll σ) = roll (fmap f σ)