There are two conventions I\'ve found in Coq\'s SSReflect extension that seem particularly useful but which I haven\'t seen widely adopted in newer dependently-typed languages (
I can provide some thoughts on the first point (defining predicates as boolean-returning functions). My biggest issue with this approach is: then it is by definition impossible for the function to have bugs, even if it turns out what it's calculating isn't what you intended it to calculate. In many cases, it would also obscure what you actually mean by the predicate if you have to include implementation details of the decision procedure for the predicate in its definition.
In mathematical applications, there will also be issues if you want to define a predicate which is a specialization of something which is not decidable in general, even if it happens to be decidable in your specific case. One example of what I'm talking about here would be defining the group with a given presentation: in Coq, a common way to define this would be the setoid with underlying set being formal expressions in the generators, and the equality given by "word equivalence". In general, this relation is not decidable, though in many specific cases it is. However, if you are restricted to defining groups with presentations where the word problem is decidable, then you lose the ability to define the unifying concept which ties all the different examples together, and prove things generically about finite presentations or about finitely presented groups. On the other hand, defining the word equivalence relation as an abstract Prop
or equivalent is straightforward (if possibly a little long).
Personally, I prefer to give the most transparent possible definition of the predicate first, and then provide decision procedures where possible (functions returning values of type {P} + {~P}
is my preference here, though the boolean-returning functions would work well too). Coq's typeclass mechanism could provide a convenient way to register such decision procedures; for example:
Class Decision (P : Prop) : Set :=
decide : {P} + {~P}.
Arguments decide P [Decision].
Instance True_dec : Decision True := left _ I.
Instance and_dec (P Q : Prop) `{Decision P} `{Decision Q} :
Decision (P /\ Q) := ...
(* Recap standard library definition of Forall *)
Inductive Forall {A : Type} (P : A->Prop) : list A -> Prop :=
| Forall_nil : Forall P nil
| Forall_cons : forall h t, P h -> Forall P t -> Forall P (cons h t).
(* Or, if you prefer:
Fixpoint Forall {A : Type} (P : A->Prop) (l : list A) : Prop :=
match l with
| nil => True
| cons h t => P h /\ Forall P t
end. *)
Program Fixpoint Forall_dec {A : Type} (P : A->Prop)
`{forall x:A, Decision (P x)} (l : list A) :
Decision (Forall P l) :=
match l with
| nil => left _ _
| cons h t => if decide (P h) then
if Forall_dec P t then
left _ _
else
right _ _
else
right _ _
end.
(* resolve obligations here *)
Existing Instance Forall_dec.