/// Curry-Howard correspondence: propositions are types, proofs are programs.
///
/// - A -> B corresponds to a function `fn(A) -> B`
/// - A /\ B (And) corresponds to a pair `(A, B)`
/// - A \/ B (Or) corresponds to `Result<A, B>` or an enum
/// - True corresponds to `()` (unit โ trivially inhabited)
/// - False corresponds to `!` (never โ uninhabited)
/// - Not A corresponds to `fn(A) -> !` (a function to Never)
///
/// A "proof" of a proposition is a value of the corresponding type.
/// If a type is inhabited (has a value), the proposition is provable.
// โโ Conjunction (A โง B) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
/// Proof of "A and B": we must provide both A and B.
struct And<A, B>(A, B);
fn and_intro<A, B>(a: A, b: B) -> And<A, B> {
And(a, b)
}
fn and_elim_left<A: Clone, B>(proof: &And<A, B>) -> A {
proof.0.clone()
}
fn and_elim_right<A, B: Clone>(proof: &And<A, B>) -> B {
proof.1.clone()
}
// โโ Disjunction (A โจ B) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
/// Proof of "A or B": we provide either A or B (but not necessarily both).
/// In Rust, `Result<A, B>` (or any enum) serves this role.
/// We use our own type for clarity.
enum Or<A, B> {
Left(A),
Right(B),
}
fn or_intro_left<A, B>(a: A) -> Or<A, B> {
Or::Left(a)
}
fn or_intro_right<A, B>(b: B) -> Or<A, B> {
Or::Right(b)
}
fn or_elim<A, B, C>(or: Or<A, B>, f: impl Fn(A) -> C, g: impl Fn(B) -> C) -> C {
match or {
Or::Left(a) => f(a),
Or::Right(b) => g(b),
}
}
// โโ Implication (A โ B) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
/// Modus ponens: given a proof of A->B and a proof of A, produce proof of B.
fn modus_ponens<A, B>(f: impl Fn(A) -> B, a: A) -> B {
f(a)
}
// โโ Negation (ยฌA = A โ โฅ) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
/// Negation as a function to the never type.
/// In stable Rust, `!` (Never) cannot be used in all positions yet,
/// so we use a phantom approach. In nightly `!` is fully usable.
///
/// We model ยฌA as: a proof that A leads to contradiction.
/// Since we can't easily use `!` as a return type in stable Rust for trait objects,
/// we demonstrate the concept with a marker.
struct Not<A>(std::marker::PhantomData<fn(A) -> std::convert::Infallible>);
/// Double negation introduction: A โข ยฌยฌA
/// Given a proof of A, we can always produce a proof of ยฌยฌA.
/// (The converse โ double-negation elimination โ requires classical logic.)
fn double_neg_intro<A>(_proof_of_a: &A) -> Box<dyn Fn(&dyn Fn(&A)) -> ()> {
// ยฌยฌA = (A -> False) -> False
// We return a function that takes (ยฌA) and applies it to our A.
// In constructive logic, this is valid: given A, give me ยฌA and I'll derive โฅ.
Box::new(|_neg_a: &dyn Fn(&A)| {
// neg_a(_proof_of_a) would be โฅ, but we can't fully express this without !
// The type-level encoding shows the structure is correct.
})
}
// โโ Some actual proofs โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
/// Proof: A โง B โ B โง A (conjunction is commutative)
fn and_comm<A: Clone, B: Clone>(proof: And<A, B>) -> And<B, A> {
And(proof.1, proof.0)
}
/// Proof: A โ A (identity / reflexivity)
fn identity_proof<A>(a: A) -> A {
a
}
/// Proof: A โง (A โ B) โ B (modus ponens as a conjunction)
fn and_modus_ponens<A: Clone, B>(proof: And<A, impl Fn(A) -> B>) -> B {
(proof.1)(proof.0)
}
/// Proof: (A โจ B) โง ยฌA โ B
/// If we have "A or B" and "not A", then we must have B.
fn disjunctive_syllogism<A, B>(
or_proof: Or<A, B>,
_refutation: impl Fn(A) -> B, // ยฌA = A -> contradiction-ish
) -> B {
match or_proof {
Or::Left(a) => _refutation(a),
Or::Right(b) => b,
}
}
fn main() {
println!("=== Curry-Howard Correspondence ===\n");
println!("Propositions are types. Proofs are programs.\n");
// Proof of "Int AND String"
let conj = and_intro(42_i32, "hello");
let left = and_elim_left(&conj);
let right = and_elim_right(&conj);
println!("And(42, \"hello\"): left={}, right={}", left, right);
// Commutativity of And
let swapped = and_comm(and_intro(1_i32, "world"));
println!("and_comm: ({}, {})", swapped.0, swapped.1);
// Proof of "Int OR String" via left injection
let disj: Or<i32, &str> = or_intro_left(42);
let result = or_elim(disj, |n| format!("number: {}", n), |s| format!("string: {}", s));
println!("Or::Left(42): {}", result);
// Modus ponens: we have proof of A and proof of A->B, so B
let mp_result = modus_ponens(|n: i32| n * 2, 21);
println!("Modus ponens (21 * 2 = {})", mp_result);
// Identity: proof that A -> A always holds
let id_result = identity_proof(99_i32);
println!("Identity proof: {}", id_result);
println!();
println!("Key insight: if a function type is inhabited (has an implementation),");
println!("the corresponding logical implication is provable.");
println!("fn(A) -> B โก A implies B");
println!("fn(A) -> ! โก A is false (A implies absurdity)");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_and_intro_elim() {
let proof = and_intro(10_i32, "ten");
assert_eq!(and_elim_left(&proof), 10);
assert_eq!(and_elim_right(&proof), "ten");
}
#[test]
fn test_and_comm() {
let proof = and_intro(1_i32, 2_i32);
let swapped = and_comm(proof);
assert_eq!(swapped.0, 2);
assert_eq!(swapped.1, 1);
}
#[test]
fn test_or_elim() {
let left: Or<i32, &str> = or_intro_left(42);
let r = or_elim(left, |n| n * 2, |_| 0);
assert_eq!(r, 84);
let right: Or<i32, i32> = or_intro_right(7);
let r2 = or_elim(right, |n| n + 100, |n| n + 1);
assert_eq!(r2, 8);
}
#[test]
fn test_modus_ponens() {
let result = modus_ponens(|s: &str| s.len(), "hello");
assert_eq!(result, 5);
}
#[test]
fn test_identity() {
assert_eq!(identity_proof(42_i32), 42);
assert_eq!(identity_proof("hello"), "hello");
}
}
(* Curry-Howard: propositions are types, proofs are programs.
A -> B corresponds to a function a -> b.
A /\ B corresponds to a pair (a, b).
A \/ B corresponds to a sum type (Left a | Right b). *)
(* Conjunction = product *)
type ('a, 'b) conj = And of 'a * 'b
let and_intro a b = And (a, b)
let and_elim_left (And (a, _)) = a
let and_elim_right (And (_, b)) = b
(* Disjunction = sum *)
type ('a, 'b) disj = Left of 'a | Right of 'b
let or_intro_left a = Left a
let or_intro_right b = Right b
let or_elim f g = function Left a -> f a | Right b -> g b
(* Implication = function *)
let modus_ponens f a = f a (* A -> B, A |- B *)
(* Negation = A -> False (using unit as False placeholder) *)
type 'a neg = 'a -> unit
(* Double negation intro *)
let dne : 'a -> 'a neg neg = fun a k -> k a
let () =
(* Proof: A /\ B -> B /\ A (and is commutative) *)
let and_comm (And (a, b)) = And (b, a) in
let proof = and_comm (And (1, "hello")) in
Printf.printf "and_comm: (%s, %d)\n" (and_elim_left proof) (and_elim_right proof);
(* Proof: A -> A (identity) *)
let id_proof : 'a -> 'a = modus_ponens (fun x -> x) in
Printf.printf "identity proof: %d\n" (id_proof 42);
Printf.printf "Curry-Howard: types are propositions, functions are proofs\n"