type_proof/
formula.rs

1//! Defines propositional formulas as types.
2//!
3//! # Language
4//!
5//! Our logical language consists of the the following symbols:
6//! - Propositional variables `p0, p1, p2, ...`
7//! - Logical negation `¬`
8//! - Logical implication `->`
9//! - Parentheses `( )`
10//!
11//! Note that `{¬, ->}` is a [functionally complete](https://en.wikipedia.org/wiki/Functional_completeness)
12//! set of Boolean operators, so other common operators (e.g. `∧, ∨`) can be expressed in terms of them.
13//!
14//! # Formulas
15//!
16//! A propositional formula is defined precisely using the following rules:
17//!
18//! - All propositional variables (type [`Var<N>`])
19//! - `¬P`, where `P` is a formula (type [`Not<P>`])
20//! - `(P -> Q)` where `P, Q` are formulas (type [`Implies<P, Q>`])
21//!
22//! The parentheses around `P -> Q` are to ensure unique readability / make operator precedence explicit. This
23//! also means that there's a simple bijection between the Rust types defined above and well-formed formulas in
24//! our language.
25//!
26//! There are some predefined type aliases for [`Var<N>`] for the first few natural numbers - for example, [`P0`]
27//! is an alias for [`Var<N0>`]. Propositional variables with arbitrarily large indices can be created using the
28//! [`P!`] macro.
29//!
30//! Some operators expressable in terms of `¬, ->` are defined via type aliases - for example, `∧` has the alias
31//! [`And<P, Q>`].
32//!
33//! ## Example
34//!
35//! The formula `(p0 -> ¬p2)` can be defined as a type like so:
36//!
37//! ```
38//! use type_proof::formula::{Implies, Not, P0, P2};
39//!
40//! type P = Implies<P0, Not<P2>>;
41//! ```
42//!
43//! # Valuations
44//!
45//! Given an assignment of [`True`] or [`False`] to each propositional variable in a formula, the formula can then
46//! be assigned a value of [`True`] or [`False`], using the following rules:
47//!
48//! ```text
49//! |   P   |  ¬P   |
50//! | ----- | ----- |
51//! | False | True  |
52//! | True  | False |
53//!
54//! |   P   |   Q   | (P -> Q) |
55//! | ----- | ----- | -------- |
56//! | False | False |   True   |
57//! | False | True  |   True   |
58//! | True  | False |   False  |
59//! | True  | True  |   True   |
60//! ```
61//!
62//! Valuations can be done via the [`Valuation`] trait.
63//!
64//! ## Example
65//!
66//! To find the value of `(p0 -> ¬p2)` under the assignment `p0 = True, p2 = False`:
67//!
68//! ```
69//! use type_proof::{
70//!     boolean::{False, True},
71//!     formula::{Implies, Not, P0, P2, Valuation},
72//!     type_utils::assert_type_eq,
73//! };
74//!
75//! // The formula (p0 -> ¬p2)
76//! type P = Implies<P0, Not<P2>>;
77//!
78//! // Our valuation
79//! struct V;
80//! impl Valuation<P0> for V {
81//!     type Value = True;
82//! }
83//! impl Valuation<P2> for V {
84//!     type Value = False;
85//! }
86//!
87//! // V automatically implements Valuation<P> given Valuation<P0> and Valuation<P2>
88//! type ValueOfP = <V as Valuation<P>>::Value;
89//! assert_type_eq::<ValueOfP, True>();
90//! ``````
91//!
92//! [`True`]: crate::boolean::True
93//! [`False`]: crate::boolean::False
94
95use std::marker::PhantomData;
96
97use crate::{
98    boolean::{self, Bool},
99    peano::{N0, N1, N2, N3, N4, N5, N6, N7, N8, N9, N10, Nat},
100};
101
102pub use type_proof_macros::P;
103
104/// A propositional formula, encoded as a type.
105pub trait Formula {
106    /// Displays the formula as a string.
107    fn display() -> String;
108}
109
110/// A propositional variable.
111///
112/// `N` is the index of the propositional variable.
113pub struct Var<N>
114where
115    N: Nat,
116{
117    _n: PhantomData<N>,
118}
119
120impl<N> Formula for Var<N>
121where
122    N: Nat,
123{
124    fn display() -> String {
125        format!("p{}", N::VALUE)
126    }
127}
128
129/// The propositional variable `p0`.
130pub type P0 = Var<N0>;
131
132/// The propositional variable `p1`.
133pub type P1 = Var<N1>;
134
135/// The propositional variable `p2`.
136pub type P2 = Var<N2>;
137
138/// The propositional variable `p3`.
139pub type P3 = Var<N3>;
140
141/// The propositional variable `p4`.
142pub type P4 = Var<N4>;
143
144/// The propositional variable `p5`.
145pub type P5 = Var<N5>;
146
147/// The propositional variable `p6`.
148pub type P6 = Var<N6>;
149
150/// The propositional variable `p7`.
151pub type P7 = Var<N7>;
152
153/// The propositional variable `p8`.
154pub type P8 = Var<N8>;
155
156/// The propositional variable `p9`.
157pub type P9 = Var<N9>;
158
159/// The propositional variable `p10`.
160pub type P10 = Var<N10>;
161
162/// Logical negation:
163/// `¬P`
164pub struct Not<P>
165where
166    P: Formula,
167{
168    _p: PhantomData<P>,
169}
170
171impl<P> Formula for Not<P>
172where
173    P: Formula,
174{
175    fn display() -> String {
176        format!("¬{}", P::display())
177    }
178}
179
180/// Logical implication:
181/// `P -> Q`
182pub struct Implies<P, Q>
183where
184    P: Formula,
185    Q: Formula,
186{
187    _p: PhantomData<P>,
188    _q: PhantomData<Q>,
189}
190
191impl<P, Q> Formula for Implies<P, Q>
192where
193    P: Formula,
194    Q: Formula,
195{
196    fn display() -> String {
197        format!("({} -> {})", P::display(), Q::display())
198    }
199}
200
201/// Logical conjunction (and):
202/// `P ∧ Q`
203#[allow(type_alias_bounds)]
204pub type And<P, Q>
205where
206    P: Formula,
207    Q: Formula,
208= Not<Implies<P, Not<Q>>>;
209
210/// Logical disjunction (or):
211/// `P ∨ Q`
212#[allow(type_alias_bounds)]
213pub type Or<P, Q>
214where
215    P: Formula,
216    Q: Formula,
217= Not<And<Not<P>, Not<Q>>>; // using De Morgan's laws
218
219/// Logical equivalence:
220/// `P <-> Q`
221#[allow(type_alias_bounds)]
222pub type Iff<P, Q>
223where
224    P: Formula,
225    Q: Formula,
226= And<Implies<P, Q>, Implies<Q, P>>;
227
228/// Deduces the value of an expression given the value of each of its propositional variables.
229///
230/// A valuation type should implement [`Valuation`] for each of its propositional variables, and
231/// [`Valuation`] will automatically be implemented for all formulas constructible from those variables.
232///
233/// # Example
234///
235/// See the [module documentation](self).
236pub trait Valuation<P>
237where
238    P: Formula,
239{
240    /// The value of the valuation on `P`.
241    type Value: Bool;
242}
243
244impl<V, P> Valuation<Not<P>> for V
245where
246    P: Formula,
247    V: Valuation<P>,
248{
249    type Value = boolean::Not<<V as Valuation<P>>::Value>;
250}
251
252impl<V, P, Q> Valuation<Implies<P, Q>> for V
253where
254    P: Formula,
255    Q: Formula,
256    V: Valuation<P> + Valuation<Q>,
257{
258    type Value = boolean::Implies<<V as Valuation<P>>::Value, <V as Valuation<Q>>::Value>;
259}
260
261#[cfg(test)]
262mod tests {
263    use crate::{
264        boolean::{False, True},
265        type_utils::assert_type_eq,
266    };
267
268    use super::*;
269
270    #[test]
271    fn display() {
272        type F = Not<Implies<P0, Not<P2>>>;
273        assert_eq!(F::display(), "¬(p0 -> ¬p2)");
274    }
275
276    #[test]
277    fn valuation() {
278        // ¬(p0 -> ¬p2)
279        type P = Not<Implies<P0, Not<P2>>>;
280
281        struct V1;
282        impl Valuation<P0> for V1 {
283            type Value = True;
284        }
285        impl Valuation<P2> for V1 {
286            type Value = False;
287        }
288
289        type PVal1 = <V1 as Valuation<P>>::Value;
290        assert_type_eq::<PVal1, False>();
291
292        struct V2;
293        impl Valuation<P0> for V2 {
294            type Value = False;
295        }
296        impl Valuation<P2> for V2 {
297            type Value = True;
298        }
299
300        type PVal2 = <V2 as Valuation<P>>::Value;
301        assert_type_eq::<PVal2, False>();
302    }
303
304    #[test]
305    fn var_macro() {
306        assert_type_eq::<P!(0), P0>();
307        assert_type_eq::<P!(3), P3>();
308    }
309}