问题
In Agda, it seems that often there are two ways to refine a set. One is by simply writing a function that checks if a property holds, and lift. For example:
has_true : List Bool -> Bool
has_true (true ∷ xs) = true
has_true (false ∷ xs) = has_true xs
has_true [] = false
Truthy : List Bool -> Set
Truthy list = T (has_true list)
Here, Truthy list
is a proof that a boolean list has at least one true element. The other way is by encoding that property directly, as an inductive type:
data Truthy : List Bool -> Set where
Here : (x : Bool) -> (x ≡ true) -> (xs : List Bool) -> Truthy (x ∷ xs)
There : (x : Bool) -> (xs : List Bool) -> Truthy xs -> Truthy (x ∷ xs)
Here, Truthy list
also proves the same thing.
I believe I have read a comparison before, but I don't remember. Is there a name for those different styles? What are the advantages and drawbacks of using one style over another? And is there a third option?
回答1:
So far you listed two ways of defining predicates:
A -> Bool
functions.- Inductive predicates.
I would add one more:
A -> Set
functions. Could be also called "recursively defined", or "defined by large elimination".
The third version is the following in Agda:
open import Data.Bool
open import Data.Unit
open import Data.Empty
open import Data.List
hastrue : List Bool → Set
hastrue [] = ⊥ -- empty type
hastrue (false ∷ bs) = hastrue bs
hastrue (true ∷ bs) = ⊤ -- unit type
First, let's talk about what kind of predicates are representable using the three options. Here's an ASCII table. *
is a wildcard standing for yes/no.
| P : A -> Set | P : A -> Bool | data P : A -> Set |
|-------------------|--------------|---------------|-------------------|
| Proof irrelevant | * | yes | * |
| Structural | yes | yes | * |
| Strictly positive | * | N/A | yes |
| Decidable | * | yes | * |
Proof irrelevance means that all proofs for P x
are equal. In the Bool
case, a proof is usually some p : P x ≡ true
, or p : IsTrue (P x)
with IsTrue = λ b → if b then ⊤ else ⊥
, and in both cases all proofs are equal indeed. We may or may not want predicates to be irrelevant.
Structural means that P x
can only be defined using elements of A
which are structurally smaller than x
. Functions are always structural, so if some predicate isn't, then it can only be defined inductively.
Strictly positive means that P
cannot occur recursively to the left of a function arrow. Non-strictly positive predicates are not definable inductively. An example for a proof-relevant non-strictly positive predicate is the interpretation of codes of function types:
data Ty : Set where
top : Ty
fun : Ty → Ty → Ty
⟦_⟧ : Ty → Set
⟦ top ⟧ = ⊤
⟦ fun A B ⟧ = ⟦ A ⟧ → ⟦ B ⟧ -- you can't put this in "data"
Decidable is self-explanatory; A -> Bool
functions are necessarily decidable, which makes them unsuitable for predicates which are undecidable or cannot be easily written out as a structural Bool
function. The advantage of decidability is excluded middle reasoning, which is not possible with the non-Bool
predicate definitions without postulates or additional decidability proofs.
Second, about the practical ramifications in Agda/Idris.
You can do dependent pattern matching on proofs of inductive predicates. With recursive and Boolean predicates, you have to pattern match on the
A
values first, to make predicate witnesses compute. Sometimes this makes inductive predicates convenient, e.g. you could have an enumeration type with 10 constructors, and you want a predicate to hold at only one constructor. The inductively defined predicate lets you only match on the true case, while the other versions require you to match on all cases all the time.On the other hand, Boolean and recursive predicates compute automatically as soon as you know that an
A
element has a given shape. This can be used in Agda to make type inference fill in proofs automatically, without tactics or instances. For example, a hole or implicit argument with typehastrue xs
can be solved by eta rules for pairs and the unit type, wheneverxs
is a list expression with a knowntrue
-containing prefix. This works analogously with Boolean predicates.
来源:https://stackoverflow.com/questions/52196188/what-are-the-drawbacks-of-encoding-properties-using-functions