What are the drawbacks of encoding properties using functions?

喜你入骨 提交于 2019-12-07 11:44:25
András Kovács

So far you listed two ways of defining predicates:

  1. A -> Bool functions.
  2. Inductive predicates.

I would add one more:

  1. 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 type hastrue xs can be solved by eta rules for pairs and the unit type, whenever xs is a list expression with a known true-containing prefix. This works analogously with Boolean predicates.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!