-- import Mathlib.Init.Set
Predicates
You've seen that in predicate logic, a proposition is a declarative statement that asserts that some state of affairs holds in some domain of discourse. For example, the natural number, four, is an even is a proposition. We've seen one way to formalize such a proposition using the mod operator.
4 %4: Nat2 =2: Nat00: Nat
Families of Propositions
Indeed, there is an infinite family of propositions, all just like this on~`e except for the particular number we plug in instead of four. As another example, the natural number, five, is even is also a proposition. And there's one such proposition for each and every natural number.
We can write this family of propositions by abstracting the value, four, to a variable: e.g., the natural number, n, is even, where n can be any natural number. Now we have a predicate. Applying it to a specific number then returns a proposition about that number.
We could say that applying the predicate, the natural number, n, is even, to the specific number, four, returns the proposition, the natural number, four, is even.
In Lean and related logics, we represent a predicate as a function: from one or more parameter values to propositions. Here's our simple example reformulated.
defis_even :is_even: Nat → PropNat →Nat: TypeProp := λProp: Typen =>n: Natn %n: Nat2 =2: Nat00: Natis_evenis_even: Nat → Prop44: Natis_evenis_even: Nat → Prop44: Natis_evenis_even: Nat → Prop55: Nat
You can see that is_even is a predicate by checking its type. Indeed, it's a function from a natural number to a proposition about that number: namely that the given number mod two is zero. The type of our predicate is thus Nat → Prop.
(is_even) -- Nat → Propis_even: Nat → Prop
Applying a Predicate to Arguments Yields a Proposition
Given a predicate we derive a proposition by applying it to one or more arguments of the specified types. The is_even predicate is appliable to a natural number as an argument. Here are two examples applying the is_even predicate.
is_evenis_even: Nat → Prop44: Natis_evenis_even: Nat → Prop55: Nat
Note that Lean reduced n%2 in each case to 0 or 1, leaving us with simpler propositions involving just 0 and 1.
To Satisfy a Predicate
We will say that specific parameter values satisfy a predicate if they yield a proposition that is true. In a sense, a proposition thus specifies a property (such as that of being even) that a value might or might not have. For example, four has the property of being even but five doesn't.
Predicates Specify Properties
In this way a predicate picks out the subset of parameter values with a specified property. As an example, we can make a list of natural numbers from 0 to 5, apply is_even to each, determine which resulting propositions are true, and thus pick out the natural numbers with the property of being even.
is_evenis_even: Nat → Prop0 -- ✓0: Natis_evenis_even: Nat → Prop1 -- ×1: Natis_evenis_even: Nat → Prop2 -- ✓2: Natis_evenis_even: Nat → Prop3 -- ×3: Natis_evenis_even: Nat → Prop4 -- ✓4: Natis_evenis_even: Nat → Prop5 -- ×5: Nat
Indeed, as we'll see in more depth shortly, we can understand the set of all objects having a particular property as those objects that satisfy the predicate that specifies the property. In Lean, we can specify the set of even numbers as follows, and evens here becomes nothing but a shorthand for is_even. We'll see more of this top when we get to lectures on set theory.
defevens :evens: {x : Sort u_1} → {Set : x} → sorryAx (Sort u_2) trueSet Nat := {Set: ?m.355n | is_even n }n: ?m.371evens 4evens: {x : Sort ?u.537} → {Set : x} → sorryAx (Sort ?u.536) trueevens 5evens: {x : Sort ?u.549} → {Set : x} → sorryAx (Sort ?u.548) true
Predicates of Multiple Arguments
Predicates can take any number of arguments. Here are some examples.
Ordered pairs of numbers and their squares
defsquare_pair :square_pair: Nat × Nat → PropNat ×Nat: TypeNat →Nat: TypeProp | (Prop: Typen1,n1: Natn2) =>n2: Natn2 =n2: Natn1^n1: Nat22: Natsquare_pair (square_pair: Nat × Nat → Prop1,1: Nat1) -- ✓1: Natsquare_pair (square_pair: Nat × Nat → Prop2,2: Nat4) -- ✓4: Natsquare_pair (square_pair: Nat × Nat → Prop3,3: Nat9) -- ✓9: Natsquare_pair (square_pair: Nat × Nat → Prop5,5: Nat20) -- × def20: Natsquare_pairs :square_pairs: {x : Sort u_1} → {Set : x} → sorryAx (Sort u_2) trueSet (Nat × Nat) := {Set: ?m.877p :p: Nat × NatNat ×Nat: TypeNat | square_pair p }Nat: Typesquare_pairs (3, 9)square_pairs: {x : Sort ?u.1056} → {Set : x} → sorryAx (Sort ?u.1055) truesquare_pairs (3, 10)square_pairs: {x : Sort ?u.1068} → {Set : x} → sorryAx (Sort ?u.1067) true(3,3: Nat9) ∈9: Natsquare_pairssquare_pairs: {x : Sort ?u.1107} → {Set : x} → sorryAx (Type ?u.1079) true
Here it is again but with two arguments rather than one pair. This material is new relative to that presented in class. Take an extra few minutes to study the precise differences in syntax and sense between these two examples. In one, separate arguments are packed into pairs, whereas in the second, they're not. They're disaggregated.
defsquare_pair' :square_pair': Nat → Nat → PropNat →Nat: TypeNat →Nat: TypeProp |Prop: Typen1,n1: Natn2 =>n2: Natn2 =n2: Natn1^n1: Nat22: Natsquare_pair'square_pair': Nat → Nat → Prop11: Nat1 -- ✓1: Natsquare_pair'square_pair': Nat → Nat → Prop22: Nat4 -- ✓4: Natsquare_pair'square_pair': Nat → Nat → Prop33: Nat9 -- ✓9: Natsquare_pair'square_pair': Nat → Nat → Prop55: Nat20 -- × def20: Natsquare_pairs' :square_pairs': {x : Sort u_1} → {Set : x} → sorryAx (Sort u_2) trueSet (Nat × Nat) := {Set: ?m.1425p :p: Nat × NatNat ×Nat: TypeNat | square_pair' p.1 p.2 }Nat: Typesquare_pairssquare_pairs: {x : Sort u_1} → {Set : x} → sorryAx (Sort u_2) true(3,3: Nat9) ∈9: Natsquare_pairssquare_pairs: {x : Sort ?u.1641} → {Set : x} → sorryAx (Type ?u.1613) true
When we specify multi-argument predicates our practice is to present the arguments one by one in disaggregated form. Among other things we can then more easily partially evaluate the function on any one of its actual parameters.
Pythagorean triples
defpythagorean_triple :pythagorean_triple: Nat → Nat → Nat → PropNat →Nat: TypeNat →Nat: TypeNat →Nat: TypeProp |Prop: Typeh,h: Natx,x: Naty =>y: Nath^h: Nat2 =2: Natx^x: Nat2 +2: Naty^y: Nat22: Natpythagorean_triplepythagorean_triple: Nat → Nat → Nat → Prop55: Nat44: Nat3 def3: Natpy_trips :py_trips: {x : Sort u_1} → {Set : x} → sorryAx (Sort u_2) trueSet (Nat × Nat × Nat) := {Set: ?m.2035t | t.1^2 = t.2.1^2 + t.2.2^2}t: ?m.2051py_trips (5,4,3)py_trips: {x : Sort ?u.2213} → {Set : x} → sorryAx (Sort ?u.2212) true
Homework
(1) Define a predicate, ev_len_str, expressing the property of a string of being of an even-length.
-- Here defev_len_str :ev_len_str: String → PropString →String: TypeProp |Prop: Types =>s: Strings.s: Stringlength %length: String → Nat2 =2: Nat0 /- (2) Use #check to typecheck an expression for the set of all even length strings. -/ #check {0: Nats :s: StringString | ev_len_str s } -- Here /- (3) Define a predicate, str_eq_len, applicable to any String value, s, and to any Nat value, n, that is satisfied just in those cases where s.length equals n. -/ defString: Typestr_eq_len :str_eq_len: String → Nat → PropString →String: TypeNat →Nat: TypeProp |Prop: Types,s: Stringn =>n: Nats.s: Stringlength =length: String → Natn -- Here /- (4) Define str_eq_lens : set String × Nat, to be the *set* of all ordered pairs, p = ⟨ s, n ⟩, such that n = s.length. -/ -- Here defn: Natstr_eq_lens :str_eq_lens: {x : Sort u_1} → {Set : x} → sorryAx (Sort u_2) trueSet (String × Nat) := {Set: ?m.2407p | str_eq_len p.1 p.2} /- (5) Use "example" in Lean to state and prove the proposition that ⟨ "I love Logic!", 13 ⟩ ∈ str_eq_lens. -/ -- Herep: ?m.2423example :example: sorryAx Prop true∈str_eq_lens :=str_eq_lens: {x : Sort ?u.2598} → {Set : x} → sorryAx (Type ?u.2585) truerfl /- (6) Use "example" in Lean again to state and prove that ⟨ "I love Logic!", 1 ⟩ ∉ str_eq_lens. That's shorthand notation for ¬("I love Logic!", 1⟩ ∈ str_eq_lens. And you know what that means. -/ -- Hererfl: ∀ {α : Sort ?u.2606} {a : α}, a = aexample :example: ¬sorryAx Prop true∉str_eq_lens := λ (str_eq_lens: {x : Sort ?u.2730} → {Set : x} → sorryAx (Type ?u.2723) truet :t: sorryAx Prop true∈str_eq_lens) =>str_eq_lens: {x : Sort ?u.2752} → {Set : x} → sorryAx (Type ?u.2739) truett: sorryAx Prop true
(7) Write a formal definition, in Lean, of party, as a set of objects of type Person. Make the Person type inhabited by giving it the single constructor, Person.jim. Hi, jim. Optionally use "structure" for this type, even if you don't know how to change the default constructor name, mk, to jim.
-- Here