; SPDX-FileCopyrightText: In 2026, Chris Pressey, the creator of this work, placed it into the public domain.
; For more information, please refer to <https://unlicense.org/>
; SPDX-License-Identifier: Unlicense

; example usage with Chicken Scheme: csi -q -b hilbert-system.scm

(load "define-opaque-0.3.scm")

(define-opaque-adt theorem make-theorem open-theorem formula 'nil
  (
    (a1 (lambda (phi psi)
      (make-theorem (list '-> phi (list '-> psi phi)))))

    (a2 (lambda (phi psi chi)
      (make-theorem (list '->
        (list '-> phi (list '-> psi chi))
        (list '-> (list '-> phi psi) (list '-> phi chi))))))

    ; Modus ponens: from a proof of P and a proof of P->Q, derive a
    ; proof of Q.
    ;
    ; open-theorem verifies that thm-p and thm-impl are genuine theorems.
    ; Passing any non-theorem value here raises an error.
    (modus-ponens (lambda (thm-p thm-impl)
      (let* (
        (given-p    (open-theorem thm-p))
        (given-impl (open-theorem thm-impl))
      )
        (if (and (pair? given-impl)
                 (equal? (car given-impl) '->)
                 (equal? (cadr given-impl) given-p))
          (make-theorem (caddr given-impl))
          (error "Cannot apply modus ponens:" given-p given-impl)))))

    (repr (lambda () formula))
  )
)

(define demo (lambda ()
  ; Derive p -> p using only A1, A2, and modus ponens.
  (let* (
    ; t1: p -> (p -> p)         by A1(p, p)
    (t1 (theorem 'a1 'p 'p))
    ; t2: p -> ((p->p) -> p)    by A1(p, p->p)
    (t2 (theorem 'a1 'p '(-> p p)))
    ; t3: (p -> ((p->p) -> p)) -> ((p -> (p->p)) -> (p -> p))
    ;                            by A2(p, p->p, p)
    (t3 (theorem 'a2 'p '(-> p p) 'p))
    ; t4: (p -> (p->p)) -> (p -> p)   by MP(t2, t3)
    (t4 (theorem 'modus-ponens t2 t3))
    ; t5: p -> p                by MP(t1, t4)
    (t5 (theorem 'modus-ponens t1 t4))
  )
    (display "Derived theorem: ")
    (display (t5 'repr))
    (newline)
  )))

(demo)

; --------------------------------------------------------------------------

; Demonstration that other data structures are rejected (even in the case
; that they define the same operations - duck-typing would not reject this.)

(define-opaque-adt ersatz-theorem make-theorem open-theorem formula 'nil
  (
    (a1 (lambda (phi psi)
      (make-theorem (list '-> phi (list '-> psi phi)))))

    (a2 (lambda (phi psi chi)
      (make-theorem (list '->
        (list '-> phi (list '-> psi chi))
        (list '-> (list '-> phi psi) (list '-> phi chi))))))

    (modus-ponens (lambda (thm-p thm-impl)
      (let* (
        (given-p    (open-theorem thm-p))
        (given-impl (open-theorem thm-impl))
      )
        (if (and (pair? given-impl)
                 (equal? (car given-impl) '->)
                 (equal? (cadr given-impl) given-p))
          (make-theorem (caddr given-impl))
          (error "Cannot apply modus ponens:" given-p given-impl)))))
  )
)

(define errorful-demo (lambda ()
  (let* (
    (t1 (theorem 'a1 'p 'p))
    (t2 (theorem 'a1 'p '(-> p p)))
    (t3 (theorem 'a2 'p '(-> p p) 'p))
    (t4 (theorem 'modus-ponens t2 t3))
    (t5 (ersatz-theorem 'modus-ponens t1 t4))
  )
    (display "Derived theorem: ")
    (display (t5 'repr))
    (newline)
  )))
