What are inductive predicates? - predicate

How would you explain inductive predicates? What are they used for? What's the theory behind them? Are they only present in dependent type systems, or in other systems as well? Are they related to GADT's in some way? Why are they true by default in Coq?
This is an example from Coq:
Inductive even : nat -> Prop :=
| even0 : even 0
| evens : forall p:nat, even p -> even (S (S P))
How would you use this definition? Is it a datatype or a proposition?

I think we call inductive predicates objects that are defined inductively and sorted in Prop.
They are used for defining properties inductively (duh). The theory behind can probably be found in the literature for inductive constructions. Search papers about CIC (the Calculus of Inductive Constructions) for instance.
They are somewhat related to GADTs, though with dependent types they can express more things. If I am not mistaken, with GADTs each constructor lives in one particular family, whereas the addition of dependent types allows constructors to inhabit different families based on their arguments.
I do not know what you mean by "true by default in Coq".
even as you define it is an inductive datatype. It is not a proposition yet, as the type nat -> Prop indicates. A proposition would be even 0 or even 1. It can be inhabited (provable) as in even 0, or not as in even 1.

To answer another part of the question. Inductive predicates/definitions are not only present in dependent type systems (e.g., Isabelle/HOL also has them). In principle they are much older and just describe the least predicate (or set) that is closed under some given inference rules. Whether this is well-defined or not depends on the rules. There are sufficient conditions which are easy to check (e.g., that the defined predicate itself only occurs positively in premises of rules). Then the Knaster-Tarski theorem yields the desired result.
If I remember correctly the book The Formal Semantics of Programming Languages: An Introduction by Glynn Winskel gives an introduction to the mathematical foundations.

Related

Is there a simpler type system with the practical utilities of CoC? [duplicate]

The article Simpler, Easier! claims it could be possible to encode dependent type systems even without the presence of "Pi" - that is, you could reuse the "Lam" constructor for it. But how can that be true, if "Pi" and "Lam" are treated differently in some cases?
Moreover, could "Star" be removed? I think you could replace all occurrences of it by "λ x . x" (id).
That's just overloading like (a, b) in Haskell: it can be both a type and a value. You can use the same binder for Π and λ and typechecker will decide from the context which one you mean. If you typecheck one binder against another, then the former is λ and the latter is Π — and that's why you can't unambiguously replace * with λ x . x — because then the former binder could be Π and the latter is * (* as a binder doesn't make any sense to me). There is a bigger problem with ∀ = λ and * = λ x . x: by transitivity * = ∀ x . x, which is a common way to postulate False — this type must be uninhabited in a sound system, so you won't have any types at all.
There was a recent thread on Coq-club "Similarities between forall and fun" (gmane.org gives me "No such message", is it just me?), here are some excerpts:
Dominic Mulligan:
And here is another with a small bibliography pointing to similar work:
http://www.macs.hw.ac.uk/~fairouz/forest/papers/journals-publications/jfp05.pdf
Ironically, according to that paper Coquand first presented the Calculus of
Constructions with a single, unified binder, following a convention
established by De Bruijn in AutoMath.
Thorsten Altenkirch:
A function and its type are very different concepts even if they have
some superficial syntactic similarity.
Especially for the newcomer this identification is very confusing and
completely misleading. I do think that one should understand type
theoretical concepts from what they mean and not how they look like.
Andreas Abel:
My student Matthias Benkard also worked on such a system, see "Type
Checking without Types"
http://www.cse.chalmers.se/~abela/benkardThesis.pdf
Note that the system described at the first link has Π-reduction (i.e. you can apply pi-types just like lambdas) — your system will have it too, if you unify Π and λ internally (as opposed to syntactically). And the system described at the second link unifies types and values
One immediate consequence is the absence of any distinction between
types and their inhabitants: Every value is a type containing itself
and all of its parts; and conversely, every type is a composite value
consisting of its inhabitants.
so there is really just one binder (except for let and maybe fix).

What is predicativity?

I have pretty decent intuition about types Haskell prohibits as "impredicative": namely ones where a forall appears in an argument to a type constructor other than ->. But just what is predicativity? What makes it important? How does it relate to the word "predicate"?
The central question of these type systems is: "Can you substitute a polymorphic type in for a type variable?". Predicative type systems are the no-nonsense schoolmarm answering, "ABSOLUTELY NOT", while impredicative type systems are your carefree buddy who thinks that sounds like a fun idea and what could possibly go wrong?
Now, Haskell muddies the discussion a bit because it believes polymorphism should be useful but invisible. So for the remainder of this post, I will be writing in a dialect of Haskell where uses of forall are not just allowed but required. This way we can distinguish between the type a, which is a monomorphic type which draws its value from a typing environment that we can define later, and the type forall a. a, which is one of the harder polymorphic types to inhabit. We'll also allow forall to go pretty much anywhere in a type -- as we'll see, GHC restricts its type syntax as a "fail-fast" mechanism rather than as a technical requirement.
Suppose we have told the compiler id :: forall a. a -> a. Can we later ask to use id as if it had type (forall b. b) -> (forall b. b)? Impredicative type systems are okay with this, because we can instantiate the quantifier in id's type to forall b. b, and substitute forall b. b for a everywhere in the result. Predicative type systems are a bit more wary of that: only monomorphic types are allowed in. (So if we had a particular b, we could write id :: b -> b.)
There's a similar story about [] :: forall a. [a] and (:) :: forall a. a -> [a] -> [a]. While your carefree buddy may be okay with [] :: [forall b. b] and (:) :: (forall b. b) -> [forall b. b] -> [forall b. b], the predicative schoolmarm isn't, so much. In fact, as you can see from the only two constructors of lists, there is no way to produce lists containing polymorphic values without instantiating the type variable in their constructors to a polymorphic value. So although the type [forall b. b] is allowed in our dialect of Haskell, it isn't really sensible -- there's no (terminating) terms of that type. This motivates GHC's decision to complain if you even think about such a type -- it's the compiler's way of telling you "don't bother".*
Well, what makes the schoolmarm so strict? As usual, the answer is about keeping type-checking and type-inference doable. Type inference for impredicative types is right out. Type checking seems like it might be possible, but it's bloody complicated and nobody wants to maintain that.
On the other hand, some might object that GHC is perfectly happy with some types that appear to require impredicativity:
> :set -Rank2Types
> :t id :: (forall b. b) -> (forall b. b)
{- no complaint, but very chatty -}
It turns out that some slightly-restricted versions of impredicativity are not too bad: specifically, type-checking higher-rank types (which allow type variables to be substituted by polymorphic types when they are only arguments to (->)) is relatively simple. You do lose type inference above rank-2, and principal types above rank-1, but sometimes higher rank types are just what the doctor ordered.
I don't know about the etymology of the word, though.
* You might wonder whether you can do something like this:
data FooTy a where
FooTm :: FooTy (forall a. a)
Then you would get a term (FooTm) whose type had something polymorphic as an argument to something other than (->) (namely, FooTy), you don't have to cross the schoolmarm to do it, and so the belief "applying non-(->) stuff to polymorphic types isn't useful because you can't make them" would be invalidated. GHC doesn't let you write FooTy, and I will admit I'm not sure whether there's a principled reason for the restriction or not.
(Quick update some years later: there is a good, principled reason that FooTm is still not okay. Namely, the way that GADTs are implemented in GHC is via type equalities, so the expanded type of FooTm is actually FooTm :: forall a. (a ~ forall b. b) => FooTy a. Hence to actually use FooTm, one would indeed need to instantiate a type variable with a polymorphic type. Thanks to Stephanie Weirich for pointing this out to me.)
Let me just add a point regarding the "etymology" issue, since the other answer by #DanielWagner covers much of the technical ground.
A predicate on something like a is a -> Bool. Now a predicate logic is one that can in some sense reason about predicates -- so if we have some predicate P and we can talk about, for a given a, P(a), now in a "predicate logic" (such as first-order logic) we can also say ∀a. P(a). So we can quantify over variables and discuss the behavior of predicates over such things.
Now, in turn, we say a statement is predicative if all of the things a predicate is applied to are introduced prior to it. So statements are "predicated on" things that already exist. In turn, a statement is impredicative if it can in some sense refer to itself by its "bootstraps".
So in the case of e.g. the id example above, we find that we can give a type to id such that it takes something of the type of id to something else of the type of id. So now we can give a function a type where an quantified variable (introduced by forall a.) can "expand" to be the same type as that of the entire function itself!
Hence impredicativity introduces a possibility of a certain "self reference". But wait, you might say, wouldn't such a thing lead to contradiction? The answer is: "well, sometimes." In particular, "System F" which is the polymorphic lambda calculus and the essential "core" of GHC's "core" language allows a form of impredicativity that nonetheless has two levels -- the value level, and the type level, which is allowed to quantify over itself. In this two-level stratification, we can have impredicativity and not contradiction/paradox.
Although note that this neat trick is very delicate and easy to screw up by the addition of more features, as this collection of articles by Oleg indicates: http://okmij.org/ftp/Haskell/impredicativity-bites.html
I'd like to make a comment on the etymology issue, since #sclv's answer isn't quite right (etymologically, not conceptually).
Go back in time, to the days of Russell when everything is set theory— including logic. One of the logical notions of particular import is the "principle of comprehension"; that is, given some logical predicate φ:A→2 we would like to have some principle to determine the set of all elements satisfying that predicate, written as "{x | φ(x) }" or some variation thereon. The key point to bear in mind is that "sets" and "predicates" are viewed as being fundamentally different things: predicates are mappings from objects to truth values, and sets are objects. Thus, for example, we may allow quantifying over sets but not quantifying over predicates.
Now, Russell was rather concerned by his eponymous paradox, and sought some way to get rid of it. There are numerous fixes, but the one of interest here is to restrict the principle of comprehension. But first, the formal definition of the principle: ∃S.∀x.S x ↔︎ φ(x); that is, for our particular φ there exists some object (i.e., set) S such that for every object (also a set, but thought of as an element) x, we have that S x (you can think of this as meaning "x∈S", though logicians of the time gave "∈" a different meaning than mere juxtaposition) is true just in case φ(x) is true. If we take the principle exactly as written then we end up with an impredicative theory. However, we can place restrictions on which φ we're allowed to take the comprehension of. (For example, if we say that φ must not contain any second-order quantifiers.) Thus, for any restriction R, if a set S is determined (i.e., generated via comprehension) by some R-predicate, then we say that S is "R-predicative". If every set in our language is R-predicative then we say that our language is "R-predicative". And then, as is often the case with hyphenated prefix things, the prefix gets dropped off and left implicit, whence "predicative" languages. And, naturally, languages which are not predicative are "impredicative".
That's the old school etymology. Since those days the terms have gone off and gotten lives of their own. The ways we use "predicative" and "impredicative" today are quite different, because the things we're concerned about have changed. So it can sometimes be a bit hard to see how the heck our modern usage ties back to this stuff. Honestly, I don't think knowing the etymology really helps any in terms of figuring out what the words are really about (these days).

A theorem prover / proof assistant supporting (multiple) subtyping / subclassing [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 8 years ago.
Improve this question
In short, I am looking for a theorem prover which its underlying logic supports multiple subtyping / subclassing mechanism.( I tried to use Isabelle, but it does not seem to provide a first class support for subtyping. see this )
I would like to define a couple of types among which some are subclasses / subtypes of others. Furthermore, each type might be subtype of more than one type. For example:
Type A
Type B
Type C
Type E
Type F
C is subtype of A
C is also subtype of B
E and F are subtypes of B
PS:
I am editing this question again to be more specific (because of a complains about being of-topic!): I am looking for a theorem prover / proof assistance in which I can define the above structure in a straight forward manner (not with workarounds as it is kindly described by some respectable answers here). If I take the types as classes then It seems above subtypings could be easily formulated in C++! So I am looking for a formal system / tool that I can define such a subtyping structure there and I can reason?
Many thanks
PVS has traditionally emphasized "predicate subtyping" a lot, but the system is a bit old-fashioned these days and has fallen behind the other big players that are more active: Coq, Isabelle/HOL, Agda, other HOLs, ACL2.
You did not make your application clear. I reckon that any of the big systems could be applied to the problem, one way or the other. Formalization is a matter to phrase your problem in a suitable way within the given logical environment. Logics are not programming languages, but have the real power of mathematics. Thus with some experience in a particular logic, you will be able to do great and amazing things that you did not expect at first sight.
When choosing your system, lists of particular low-level features are not so relevant. It is more important that you like the general style and culture of the system, before you make a commitment. You can compare that to learning a foreign language. Before you spend months or years to study do you collect features of the grammar? I don't think so.
You include the 'isabelle' tag, and it happens to be that according to "Wiki Subtyping", Isabelle provides one form of subtyping, "coercive subtyping", though as explained by Andreas Lochbihler, Isabelle doesn't really have subtyping like what you're wanting (and others want, too).
However, you're talking in vague generalities, so I easily provide a contrived example that meets the requirements of your 5 types. And though it is contrived, it's not meaningless, as I explain below.
(*The 5 types.*)
datatype tA = tA_con int rat real
type_synonym tB = "(nat * int)"
type_synonym tC = int
type_synonym tE = rat
type_synonym tF = real
(*The small amount of code required to automatically coerce from tC to tB.*)
fun coerce_C_to_B :: "tC => tB" where "coerce_C_to_B i = (0, i)"
declare [[coercion coerce_C_to_B]]
(*I can use type tC anywhere I can use type tB.*)
term "(2::tC)::tB"
value "(2::tC)::tB"
In the above example, it can be seen that types tC, tE, and tF lend themselves naturally, and easily, to being coerced to types tA or tB.
This coercion of types is done quite a bit in Isabelle. For example, the type nat is used to define int, and int is used to define rat. Consequently, nat is automatically coerced to int, though int isn't to rat.
Wrap I (you haven't been using canonical HOL):
In your previous question examples, you've been using typedecl to introduce new types, and that doesn't generally reflect how people define new types.
Types defined with typedecl are nearly always foundational and axiomatized, such as with ind, in Nat.thy.
See here: isabelle.in.tum.de/repos/isabelle/file/8f4a332500e4/src/HOL/Nat.thy#l21
The keyword datatype_new is one of the primary, automagical ways to define new types in Isabelle/HOL.
Part of the power of datatype_new (and datatype) is its use to define recursive types, and its use with fun, for example with pattern matching.
In comparison to other proof assistants, I assume that the new abilities of datatype_new is not trivial. For example, a distinguishing feature between types and ZFC sets has been that ZFC sets can be nested arbitrarily deep. Now, with datatype_new, a type of countable or finite set can be defined that can be nested arbitrarily deep.
You can use standard types, such as tuples, lists, and records to define new types, which can then be used with coercions, as shown in my example above.
Wrap II (but, yes, that would be nice):
I could have continued with the list above, but I separate from that list two other keywords to define new types, typedef and quotient_type.
I separate these two because, now, we enter into the realm of your complaint, that the logic of Isabelle/HOL doesn't make it easy, many times, to define a type/subtype relationship.
Knowing nothing much, I do know now that I should only use typedef as a last resort. It's actually used quite a bit in the HOL sources, but then, the developers then have to do a lot of work to make a type defined with it easy to use, such as with fset
http://isabelle.in.tum.de/repos/isabelle/file/8f4a332500e4/src/HOL/Library/FSet.thy
Wrap III (however, none are perfect in this imperfect world):
You listed the 3 proof assistants that probably have the largest market share, Coq, Isabelle, and Agda.
With proof assistants, we define your priorities, do our research, and then pick one, but it's like with programming languages. We're not going to get everything with any of them.
For myself, mathematical syntax and structured proofs are very important. Isabelle seems to be sufficiently powerful, so I choose it. It's not a perfect world, for sure.
Wrap IV (Haskell, Isablle, and type classes):
Isabelle, in fact, does have a very powerful form of subclassing, "type classes".
Well, it is powerful, but it is also limited in that you can only use one type variable when defining a type class.
If you look at Groups.thy, you'll see the introduction of class after class after class, to create a hierarchy of classes.
isabelle.in.tum.de/repos/isabelle/file/8f4a332500e4/src/HOL/Groups.thy
You also included the 'haskell' tag. The functional programming attributes of Isabelle/HOL, with its datatype and type classes, help tie the use of Isabelle/HOL to the use of Haskell, as demonstrated by the ability of the Isabelle code generator to produce Haskell code.
There are ways to achieve that in agda.
Group the functions related to one "type" into fields of record
Construct instances of such record for the types you want
Pass that record along into proofs that require them
For example:
record Monoid (A : Set) : Set where
constructor monoid
field
z : A
m+ : A -> A -> A
xz : (x : A) -> m+ x z == x
zx : (x : A) -> m+ z x == x
assoc : (x : A) -> (y : A) -> (z : A) -> m+ (m+ x y) z == m+ x (m+ y z)
open Monoid public
Now list-is-monoid = monoid Nil (++) lemma-append-nil lemma-nil-append lemma-append-assoc instantiates (proves) that a List is a Monoid (given the the proofs of Nil being a neutral element, and a proof of associativity).

Given a Haskell type signature, is it possible to generate the code automatically?

What it says in the title. If I write a type signature, is it possible to algorithmically generate an expression which has that type signature?
It seems plausible that it might be possible to do this. We already know that if the type is a special-case of a library function's type signature, Hoogle can find that function algorithmically. On the other hand, many simple problems relating to general expressions are actually unsolvable (e.g., it is impossible to know if two functions do the same thing), so it's hardly implausible that this is one of them.
It's probably bad form to ask several questions all at once, but I'd like to know:
Can it be done?
If so, how?
If not, are there any restricted situations where it becomes possible?
It's quite possible for two distinct expressions to have the same type signature. Can you compute all of them? Or even some of them?
Does anybody have working code which does this stuff for real?
Djinn does this for a restricted subset of Haskell types, corresponding to a first-order logic. It can't manage recursive types or types that require recursion to implement, though; so, for instance, it can't write a term of type (a -> a) -> a (the type of fix), which corresponds to the proposition "if a implies a, then a", which is clearly false; you can use it to prove anything. Indeed, this is why fix gives rise to ⊥.
If you do allow fix, then writing a program to give a term of any type is trivial; the program would simply print fix id for every type.
Djinn is mostly a toy, but it can do some fun things, like deriving the correct Monad instances for Reader and Cont given the types of return and (>>=). You can try it out by installing the djinn package, or using lambdabot, which integrates it as the #djinn command.
Oleg at okmij.org has an implementation of this. There is a short introduction here but the literate Haskell source contains the details and the description of the process. (I'm not sure how this corresponds to Djinn in power, but it is another example.)
There are cases where is no unique function:
fst', snd' :: (a, a) -> a
fst' (a,_) = a
snd' (_,b) = b
Not only this; there are cases where there are an infinite number of functions:
list0, list1, list2 :: [a] -> a
list0 l = l !! 0
list1 l = l !! 1
list2 l = l !! 2
-- etc.
-- Or
mkList0, mkList1, mkList2 :: a -> [a]
mkList0 _ = []
mkList1 a = [a]
mkList2 a = [a,a]
-- etc.
(If you only want total functions, then consider [a] as restricted to infinite lists for list0, list1 etc, i.e. data List a = Cons a (List a))
In fact, if you have recursive types, any types involving these correspond to an infinite number of functions. However, at least in the case above, there is a countable number of functions, so it is possible to create an (infinite) list containing all of them. But, I think the type [a] -> [a] corresponds to an uncountably infinite number of functions (again restrict [a] to infinite lists) so you can't even enumerate them all!
(Summary: there are types that correspond to a finite, countably infinite and uncountably infinite number of functions.)
This is impossible in general (and for languages like Haskell that does not even has the strong normalization property), and only possible in some (very) special cases (and for more restricted languages), such as when a codomain type has the only one constructor (for example, a function f :: forall a. a -> () can be determined uniquely). In order to reduce a set of possible definitions for a given signature to a singleton set with just one definition need to give more restrictions (in the form of additional properties, for example, it is still difficult to imagine how this can be helpful without giving an example of use).
From the (n-)categorical point of view types corresponds to objects, terms corresponds to arrows (constructors also corresponds to arrows), and function definitions corresponds to 2-arrows. The question is analogous to the question of whether one can construct a 2-category with the required properties by specifying only a set of objects. It's impossible since you need either an explicit construction for arrows and 2-arrows (i.e., writing terms and definitions), or deductive system which allows to deduce the necessary structure using a certain set of properties (that still need to be defined explicitly).
There is also an interesting question: given an ADT (i.e., subcategory of Hask) is it possible to automatically derive instances for Typeable, Data (yes, using SYB), Traversable, Foldable, Functor, Pointed, Applicative, Monad, etc (?). In this case, we have the necessary signatures as well as additional properties (for example, the monad laws, although these properties can not be expressed in Haskell, but they can be expressed in a language with dependent types). There is some interesting constructions:
http://ulissesaraujo.wordpress.com/2007/12/19/catamorphisms-in-haskell
which shows what can be done for the list ADT.
The question is actually rather deep and I'm not sure of the answer, if you're asking about the full glory of Haskell types including type families, GADT's, etc.
What you're asking is whether a program can automatically prove that an arbitrary type is inhabited (contains a value) by exhibiting such a value. A principle called the Curry-Howard Correspondence says that types can be interpreted as mathematical propositions, and the type is inhabited if the proposition is constructively provable. So you're asking if there is a program that can prove a certain class of propositions to be theorems. In a language like Agda, the type system is powerful enough to express arbitrary mathematical propositions, and proving arbitrary ones is undecidable by Gödel's incompleteness theorem. On the other hand, if you drop down to (say) pure Hindley-Milner, you get a much weaker and (I think) decidable system. With Haskell 98, I'm not sure, because type classes are supposed to be able to be equivalent to GADT's.
With GADT's, I don't know if it's decidable or not, though maybe some more knowledgeable folks here would know right away. For example it might be possible to encode the halting problem for a given Turing machine as a GADT, so there is a value of that type iff the machine halts. In that case, inhabitability is clearly undecidable. But, maybe such an encoding isn't quite possible, even with type families. I'm not currently fluent enough in this subject for it to be obvious to me either way, though as I said, maybe someone else here knows the answer.
(Update:) Oh a much simpler interpretation of your question occurs to me: you may be asking if every Haskell type is inhabited. The answer is obviously not. Consider the polymorphic type
a -> b
There is no function with that signature (not counting something like unsafeCoerce, which makes the type system inconsistent).

Why aren't there many discussions about co- and contra-variance in Haskell (as opposed to Scala or C#)?

I know what covariance and contravariance of types are. My question is why haven't I encountered discussion of these concepts yet in my study of Haskell (as opposed to, say, Scala)?
It seems there is a fundamental difference in the way Haskell views types as opposed to Scala or C#, and I'd like to articulate what that difference is.
Or maybe I'm wrong and I just haven't learned enough Haskell yet :-)
There are two main reasons:
Haskell lacks an inherent notion of subtyping, so in general variance is less relevant.
Contravariance mostly appears where mutability is involved, so most data types in Haskell would simply be covariant and there'd be little value to distinguishing that explicitly.
However, the concepts do apply--for instance, the lifting operation performed by fmap for Functor instances is actually covariant; the terms co-/contravariance are used in Category Theory to talk about functors. The contravariant package defines a type class for contravariant functors, and if you look at the instance list you'll see why I said it's much less common.
There are also places where the idea shows up implicitly, in how manual conversions work--the various numeric type classes define conversions to and from basic types like Integer and Rational, and the module Data.List contains generic versions of some standard functions. If you look at the types of these generic versions you'll see that Integral constraints (giving toInteger) are used on types in contravariant position, while Num constraints (giving fromInteger) are used for covariant position.
There are no "sub-types" in Haskell, so covariance and contravariance don't make any sense.
In Scala, you have e.g. Option[+A] with the subclasses Some[+A] and None. You have to provide the covariance annotations + to say that an Option[Foo] is an Option[Bar] if Foo extends Bar. Because of the presence of sub-types, this is necessary.
In Haskell, there are no sub-types. The equivalent of Option in Haskell, called Maybe, has this definition:
data Maybe a = Nothing | Just a
The type variable a can only ever be one type, so no further information about it is necessary.
As mentioned, Haskell does not have subtypes. However, if you're looking at typeclasses it may not be clear how that works without subtyping.
Typeclasses specify predicates on types, not types themselves. So when a Typeclass has a superclass (e.g. Eq a => Ord a), that doesn't mean instances are subtypes, because only the predicates are inherited, not the types themselves.
Also, co-, contra-, and in- variance mean different things in different fields of math (see Wikipedia). For example the terms covariant and contravariant are used in functors (which in turn are used in Haskell), but the terms mean something completely different. The term invariant can be used in a lot of places.

Resources