I have some Agda code like the following (trimmed down to focus on what I believe is the issue, which is the recursion using map):
open import Data.List using (List; map)
data Foo : Set where
Bar : List Foo → Foo
data Foo2 : Set where
Bar2 : List Foo2 → Foo2
process : Foo → Foo2
process (Bar x) = Bar2 (map process x)
{...}/Foo.agda:9,1-10,39
Termination checking failed for the following functions:
process
Problematic calls:
process
(at {...}/Foo.agda:10,29-36)
How do I convince Agda that this function terminates (ideally in a simple way)?
Unfortunately currently the only safe solution is to add a mutually defined version of map that is specialised.
{-# OPTIONS --safe --without-K #-}
open import Agda.Builtin.List
data Foo : Set where
Bar : List Foo → Foo
data Foo2 : Set where
Bar2 : List Foo2 → Foo2
process : Foo → Foo2
process′ : List Foo → List Foo2
process (Bar x) = Bar2 (process′ x)
process′ [] = []
process′ (f ∷ fs) = process f ∷ process′ fs
Related
I have a record type which allowes invalid instances as they might be from an external source. E.g.
record Foo : Set where
fields
x : Nat
y : Nat
z : Nat
Now I have a validator which returns a proof that a given Foo conforms to certain propositions. The type of the proof could be for example:
data IsValidFoo : Foo → Set where
validFoo : (foo : Foo) → Even (Foo.x foo) → (Foo.y foo) < (Foo.z foo) → IsValidFoo foo
validateFoo : (foo : Foo) → Maybe (IsValidFoo foo)
…
I want to be able to access the individual proofs in IsValidFoo by name and thought of converting IsValidFoo to a record type. Is this even possible and how could it be achieved given that I want its type to stay IsValidFoo : Foo → Set?
Sure, this is possible by defining IsValidFoo as a record type parametrized over foo : Foo:
open import Agda.Builtin.Nat hiding (_<_)
postulate
_<_ : Nat → Nat → Set
Even : Nat → Set
Maybe : Set → Set
record Foo : Set where
field
x : Nat
y : Nat
z : Nat
record IsValidFoo (foo : Foo) : Set where
constructor validFoo
field
even-x : Even (Foo.x foo)
y<z : (Foo.y foo) < (Foo.z foo)
validateFoo : (foo : Foo) → Maybe (IsValidFoo foo)
I'm trying to understand Agda's built in reflection mechanisms, so I decided to write a simple function that takes a string for an identifier,
a quoted type, and a quoted term, and simply defines a term of the given type with the given string identifier. Thus, I wrote the following:
testDefineName' : String → TC Type → TC Term → TC ⊤
testDefineName' s t a =
bindTC (freshName s)
(λ n → bindTC t
(λ t' → bindTC a
(λ a' → (bindTC (declareDef (arg (arg-info visible relevant) n) t')
(λ _ → defineFun n ((clause [] a') ∷ []))))))
unquoteDecl = (testDefineName' "test" (quoteTC ℕ) (quoteTC zero))
This type-checks, but, when I try to use "test" elsewhere I get a Not in scope: test error.
The documentation for unquoteDecl is kind of opaque. Appaently declarations should be of the form
unquoteDecl x_1 x_2 ... x_n = m
where the x_i are Names, and m has type TC \top, so maybe what I was trying to do isn't actually possible, but I still don't understand how this mechanism works: if m has to be of type TC ⊤, and thus cannot be a function of the names x_1 x_2 ... x_n, I don't see how it is possible to bring any new names into scope using unquoteDecl at all!
So, to sum up:
Is it possible to define a function like mine using Agda's reflection mechanism so that I can bring new names into scope using a String argument? What I want is something like:
<boilerplate code for unquoting> testDefineName' "test" (quoteTC ℕ) (quoteTC zero)
test' : ℕ
test' = test
to compile (i.e. I can actually use the new name, "test")
and if not, by what mechanism can m make use of the names x_1 ... x_n? Can m actually have a type like List Name → TC ⊤, contrary to the documentation?
Based on the way Ulf uses unquoteDecl, I have the impression that you need to list on the LHS the names which are going to extend the scope.
The problem with your setup is that you don't know the Name as you generate a fresh one from a String and have no way of getting hold of it AFAIK. I have the impression that freshName is only supposed to be used to generate internal names from inside a tactic.
If you make testDefineName' take a Name rather than a String then everything works out:
testDefineName' : Name → TC Type → TC Term → TC ⊤
testDefineName' n t a = bindTC t
$ λ t' → bindTC a
$ λ a' → bindTC (declareDef (arg (arg-info visible relevant) n) t')
$ λ _ → defineFun n ((clause [] a') ∷ [])
unquoteDecl test = testDefineName' test (quoteTC ℕ) (quoteTC zero)
I'm easily able to obtain a list of Keys, as follows:
open import Relation.Binary
open import Relation.Binary.PropositionalEquality using (_≡_)
module AVL-Tree-Functions
{ k v ℓ } { Key : Set k }
( Value : Key → Set v )
{ _<_ : Rel Key ℓ }
( isStrictTotalOrder : IsStrictTotalOrder _≡_ _<_ )
where
open import Data.AVL Value isStrictTotalOrder public
open import Data.List.Base
open import Function
open import Data.Product
keys : Tree → List Key
keys = Data.List.Base.map proj₁ ∘ toList
But I'm not clear on how to specify the type of function that returns a list of values. Here's my first attempt:
-- this fails to typecheck
values : Tree → List Value
values = Data.List.Base.map proj₂ ∘ toList
Relatedly, I'm also confused about the declaration of Value in Data.AVL. With ( Value : Key → Set v ), it looks like the type of each Value in the tree is dependent on the key? Or, something like that. I then figured that proj₂ would be returning something of type Set v, so I tried this:
-- this also fails to typecheck
values : Tree → List (Set v)
values = Data.List.Base.map proj₂ ∘ toList
But that doesn't work either (it fails with a different error). Please show how to get a list of values from a Data.AVL.Tree (or explain why it's impossible). Bonus: explain why my two attempts failed.
P.s. This is using version 2.4.2.3 of Agda and the agda-stdlib.
it looks like the type of each Value in the tree is dependent on the
key?
Yes. And that's why your code doesn't typecheck — Lists are homogeneous, but different Values have different indices (i.e. depend on different Keys) and hence different types.
You can use heterogeneous lists as in gallais' answer, but indexed lists might be enough in your case:
open import Level
data IList {ι α} {I : Set ι} (A : I -> Set α) : Set (ι ⊔ α) where
[]ᵢ : IList A
_∷ᵢ_ : ∀ {i} -> A i -> IList A -> IList A
projs₂ : ∀ {α β} {A : Set α} {B : A -> Set β} -> List (Σ A B) -> IList B
projs₂ [] = []ᵢ
projs₂ ((x , y) ∷ ps) = y ∷ᵢ projs₂ ps
Or you can combine the techniques:
data IHList {ι α} {I : Set ι} (A : I -> Set α) : List I -> Set (ι ⊔ α) where
[]ᵢ : IHList A []
_∷ᵢ_ : ∀ {i is} -> A i -> IHList A is -> IHList A (i ∷ is)
projs₂ : ∀ {α β} {A : Set α} {B : A -> Set β}
-> (xs : List (Σ A B)) -> IHList B (Data.List.Base.map proj₁ xs)
projs₂ [] = []ᵢ
projs₂ ((x , y) ∷ ps) = y ∷ᵢ projs₂ ps
What Value : Key → Set v means is that the type of the value may depend on the key it is associated to. This means that an AVL tree may contain Booleans, Nats, and so on as long as the key they are stored in reflects that fact. A bit like the fact that records can store values of different types (the types are determined by the field's name).
Now, they are different ways to do this: you can extract the content of the whole tree to a list of key / value pairs (because a list's elements are all the same, you need to build a pair here so that everything has the same type Σ Key Value). This is what toList does.
An alternative is to use what is usually called an HList (the H stands for heterogeneous) which stores in a list at the type level the type each one of its elements is supposed to have. I define it here by induction on the set of elements for size reasons but it is not at all crucial (if you were to define it as a datatype, it would live one level higher):
open import Level
open import Data.Unit
HList : {ℓ : Level} (XS : List (Set ℓ)) → Set ℓ
HList [] = Lift ⊤
HList (X ∷ XS) = X × HList XS
Now, you can give the type of the HList of values. Given t a Tree, it uses your keys to extract the list of keys and turns them into Sets by mapping Value over the list.
values : (t : Tree) → HList (List.map Value (keys t))
Extracting the values can then be done with the help of an auxiliary function working along the list produced by toList:
values t = go (toList t) where
go : (kvs : List (Σ Key Value)) → HList (List.map Value $ List.map proj₁ kvs)
go [] = lift tt
go (kv ∷ kvs) = proj₂ kv , go kvs
Here's what I understand about Relation.Binary.PropositionalEquality.TrustMe.trustMe: it seems to take an arbitrary x and y, and:
if x and y are genuinely equal, it becomes refl
if they are not, it behaves like postulate lie : x ≡ y.
Now, in the latter case it can easily make Agda inconsistent, but this in itself is not so much a problem: it just means that any proof using trustMe is a proof by appeal to authority. Moreover, though you can use such things to write coerce : {A B : Set} -> A -> B, it turns out to be the case that coerce {ℕ} {Bool} 0 doesn't reduce (at least, not according to C-c C-n), so it's really not analogous to, say, Haskell's semantic-stomping unsafeCoerce.
So what do I have to fear from trustMe? On the other hand, is there ever a reason to use it outside of implementing primitives?
Indeed, attempting to pattern match on trustMe which does not evaluate to refl results in a stuck term. Perhaps it is enlightening to see (part of) the code that defines the primitive operation behind trustMe, primTrustMe:
(u', v') <- normalise (u, v)
if (u' == v') then redReturn (refl $ unArg u) else
return (NoReduction $ map notReduced [a, t, u, v])
Here, u and v represent the terms x and y, respectively. The rest of the code can be found in the module Agda.TypeChecking.Primitive.
So yes, if x and y are not definitionally equal, then primTrustMe (and by extension trustMe) behaves as a postulate in the sense that evaluation simply gets stuck. However, there's one crucial difference when compiling Agda down to Haskell. Taking a look at the module Agda.Compiler.MAlonzo.Primitives, we find this code:
("primTrustMe" , Right <$> do
refl <- primRefl
flip runReaderT 0 $
term $ lam "a" (lam "A" (lam "x" (lam "y" refl))))
This looks suspicious: it always returns refl no matter what x and y are. Let's have a test module:
module DontTrustMe where
open import Data.Nat
open import Data.String
open import Function
open import IO
open import Relation.Binary.PropositionalEquality
open import Relation.Binary.PropositionalEquality.TrustMe
postulate
trustMe′ : ∀ {a} {A : Set a} {x y : A} → x ≡ y
transport : ℕ → String
transport = subst id (trustMe {x = ℕ} {y = String})
main = run ∘ putStrLn $ transport 42
Using trustMe inside transport, compiling the module (C-c C-x C-c) and running the resulting executable, we get... you guessed it right - a segfault.
If we instead use the postulate, we end up with:
DontTrustMe.exe: MAlonzo Runtime Error:
postulate evaluated: DontTrustMe.trustMe′
If you do not intend to compile your programs (at least using MAlonzo) then inconsistency should be your only worry (on the other hand, if you only typecheck your programs then inconsistency usually is kind of a big deal).
There are two use cases I can think of at the moment, first is (as you've said) for implementing primitives. The standard library uses trustMe in three places: in implementation of decidable equality for Names (Reflection module), Strings (Data.String module) and Chars (Data.Char module).
The second one is much like the first one, except that you provide the data type and the equality function yourself and then use trustMe to skip the proving and just use the equality function to define a decidable equality. Something like:
open import Data.Bool
open import Relation.Binary
open import Relation.Binary.PropositionalEquality
open import Relation.Nullary
data X : Set where
a b : X
eq : X → X → Bool
eq a a = true
eq b b = true
eq _ _ = false
dec-eq : Decidable {A = X} _≡_
dec-eq x y with eq x y
... | true = yes trustMe
... | false = no whatever
where postulate whatever : _
However, if you screw up eq, the compiler cannot save you.
First some boring imports:
import Relation.Binary.PropositionalEquality as PE
import Relation.Binary.HeterogeneousEquality as HE
import Algebra
import Data.Nat
import Data.Nat.Properties
open PE
open HE using (_≅_)
open CommutativeSemiring commutativeSemiring using (+-commutativeMonoid)
open CommutativeMonoid +-commutativeMonoid using () renaming (comm to +-comm)
Now suppose that I have a type indexed by, say, the naturals.
postulate Foo : ℕ -> Set
And that I want to prove some equalities about functions operating on this type Foo. Because agda is not very smart, these will be heterogeneous equalities. A simple example would be
foo : (m n : ℕ) -> Foo (m + n) -> Foo (n + m)
foo m n x rewrite +-comm n m = x
bar : (m n : ℕ) (x : Foo (m + n)) -> foo m n x ≅ x
bar m n x = {! ?0 !}
The goal in bar is
Goal: (foo m n x | n + m | .Data.Nat.Properties.+-comm n m) ≅ x
————————————————————————————————————————————————————————————
x : Foo (m + n)
n : ℕ
m : ℕ
What are these |s doing in the goal? And how do I even begin to construct a term of this type?
In this case, I can work around the problem by manually doing the substitution with subst, but that gets really ugly and tedious for larger types and equations.
foo' : (m n : ℕ) -> Foo (m + n) -> Foo (n + m)
foo' m n x = PE.subst Foo (+-comm m n) x
bar' : (m n : ℕ) (x : Foo (m + n)) -> foo' m n x ≅ x
bar' m n x = HE.≡-subst-removable Foo (+-comm m n) x
Those pipes indicate that reduction is suspended pending the result of the expressions in question, and it typically boils down to the fact that you had a with block whose result you need to know to proceed. This is because the rewrite construct just expands to a with of the expression in question along with any auxiliary values that might be needed to make it work, followed by a match on refl.
In this case, it just means that you need to introduce the +-comm n m in a with block and pattern match on refl (and you'll probably need to bring n + m into scope too, as it suggests, so the equality has something to talk about). The Agda evaluation model is fairly straightforward, and if you pattern match on something (except for the faux pattern matches on records), it won't reduce until you pattern match on that same thing. You might even be able to get away with rewriting by the same expression in your proof, since it just does what I outlined for you.
More precisely, if you define:
f : ...
f with a | b | c
... | someDataConstructor | boundButNonConstructorVariable | someRecordConstructor = ...
and then you refer to f as an expression, you will get the pipes you observed for expression a only, because it matches on someDataConstructor, so at the very least to get f to reduce you will need to introduce a and then match on someDataConstructor from it. On the other hand, b and c, although they were introduced in the same with block, do no halt evaluation, because b doesn't pattern match, and c's someRecordConstructor is known statically to be the only possible constructor because it's a record type with eta.