formal/formal.scm

231 lines
5.9 KiB
Scheme
Raw Normal View History

2025-02-20 23:11:31 -05:00
;; okay, i'll keep these procedures for the sake of nesting
2025-02-20 23:38:00 -05:00
(define (make-pand p q) `( ,p ,q))
2025-02-20 22:26:09 -05:00
(define pand
(case-lambda
[() #t]
[(p) p]
[(p . qs) (fold-left make-pand p qs)]))
2025-02-20 23:38:00 -05:00
(define (make-por p q) `( ,p ,q))
2025-02-20 22:26:09 -05:00
(define por
(case-lambda
[() #f]
[(p) p]
[(p . qs) (fold-left make-por p qs)]))
;; iirc, or: (not p) or q
2025-02-20 23:38:00 -05:00
(define (make-p-> p q) `( ,p ,q))
2025-02-20 22:26:09 -05:00
;; could fold but laziness
(define p->
(case-lambda
[() #t]
[(p) p]
[(p . qs) (make-p-> p (apply p-> qs))]))
(define (p<-> p q) (pand (p-> p q) (p-> q p)))
;; maybe could have some pretty-printing logic? this definition is just 2 good 2 pass up
(define (pnot p) (p-> p #f))
(define ~ pnot)
;; maybe a var is just a sym? no forall lel
;; existence l8r
;; nah
;; valued vars? :o
;; (define-record-type forall
;; (fields vars p)
;; (nongenerative forall))
;; (define-syntax forall
;; (syntax-rules ()
;; [(forall (var* ...) p)
;; (make-forall
;; '(var* ...)
;; (let ([var* 'var*] ...) p))]
;; [(forall var p) (forall (var) p)]))
(define-record-type argument
2025-02-20 23:11:31 -05:00
(fields premises conclusions)
2025-02-20 22:26:09 -05:00
(nongenerative argument))
(define-syntax argument
(syntax-rules ()
2025-02-20 23:11:31 -05:00
[(_ (var* ...) (premise* ...) (conclusion* ...))
2025-02-20 22:26:09 -05:00
(lambda (var* ...)
2025-02-20 23:11:31 -05:00
(make-argument (list premise* ...) (list conclusion* ...)))]))
2025-02-20 22:26:09 -05:00
(define modus-ponens
(argument (p q)
2025-02-20 23:11:31 -05:00
[(p-> p q)
p]
[q]))
2025-02-20 22:26:09 -05:00
(define modus-tollens
(argument (p q)
2025-02-20 23:11:31 -05:00
[(p-> p q)
(~ q)]
[(~ p)]))
;; YES: multiple conclusions (no more simplification-{l,r})
(define simplification
(argument (p q)
[(pand p q)]
[p q]))
(define conjunction
(argument (p q)
[p
q]
[(pand p q)]))
2025-02-20 22:26:09 -05:00
;; (follows (P* ... (-> p q) p Q* ...) q modus-ponens) => #t
;; iterative argument
;; laws of inference are taken as fact
;; norm vars
;; order irrelevant
;; search for reqs?
(define (premises<=? p1 p2)
(and (andmap (lambda (prem) (member prem p2)) p1)
#t))
2025-02-20 23:11:31 -05:00
(define (follows? prems q by)
(and (member q (argument-conclusions by))
(premises<=? (argument-premises by) prems)))
2025-02-20 22:26:09 -05:00
2025-02-20 23:11:31 -05:00
(define-record-type step
(fields conclusion rule)
(nongenerative step))
2025-02-20 22:26:09 -05:00
2025-02-20 23:11:31 -05:00
(define-record-type proof
(fields argument steps)
(nongenerative proof))
2025-02-20 22:26:09 -05:00
2025-02-20 23:11:31 -05:00
(define-syntax proof
(syntax-rules ()
[(_ (var* ...) arg (conclusion* rule*) ...)
(lambda (var* ...)
(make-proof (arg var* ...)
(list (make-step conclusion* rule*) ...)))]))
;; bleh
;; huh, procedure-arity-mask can handle 1000+ no problem
;; returns the lowest arity
(define (procedure-arity proc)
(define mask (procedure-arity-mask proc))
(let loop ([i 0])
(if (logbit? i mask)
i
(loop (add1 i)))))
2025-02-20 23:38:00 -05:00
;; so over-general lel
(define proof-variable
(let* ([bases '#(p q r s t u)]
[bases-count (vector-length bases)])
(lambda (n)
(define base (vector-ref bases (mod n bases-count)))
(define num (floor (/ n bases-count)))
(string->symbol
(format "~a~a"
base
(if (zero? num) "" num))))))
2025-02-21 00:24:13 -05:00
(define (placeholder-saturate f)
;; map iota is bad but convenient (macro so needed)
(apply f (map proof-variable (iota (procedure-arity f)))))
2025-02-20 23:38:00 -05:00
;; i think i prefer the turnstile to therefore
(define (argument-pp arg port)
(define prems (argument-premises arg))
(define concls (argument-conclusions arg))
(format port "~{~a\n~}~{⊢ ~a\n~}"
prems concls))
2025-02-21 00:24:13 -05:00
(define (general-argument-pp arg port)
(argument-pp (placeholder-saturate arg) port))
(define (cartesian-exponent xs n)
(assert (and (integer? n) (>= n 0)))
(case n
[0 '()]
[1 (map list xs)]
[else
(let ([exp-1 (cartesian-exponent xs (sub1 n))])
(apply append
(map (lambda (xs2)
(map (lambda (x)
(cons x xs2))
xs))
exp-1)))]))
2025-02-20 23:38:00 -05:00
2025-02-20 23:11:31 -05:00
(define (proof-valid? proof%)
(define (gensyms n xs)
(if (zero? n)
;; less confusing to apply (proof g0 g1 ...)
(reverse xs)
2025-02-21 00:24:13 -05:00
(gensyms (sub1 n)
(cons (gensym (symbol->string (proof-variable (sub1 n)))) xs))))
2025-02-20 23:11:31 -05:00
(define proof (apply proof% (gensyms (procedure-arity proof%) '())))
(define arg (proof-argument proof))
;; reversing here lets us recover a nice order at the end
(define prems (reverse (argument-premises arg)))
(define goals (argument-conclusions arg))
2025-02-21 00:24:13 -05:00
(define (fail concl rule)
(printf "Failed to apply rule:\n")
(argument-pp rule #t)
(printf "Proof state:\n~{~a\n~}⊢ ~a\n"
(reverse prems) concl)
#f)
(define (handle-general-step concl rule)
(define targets
(apply append prems (map (lambda (p) (if (pair? p) (cdr p) '())) prems)))
(ormap
(lambda (ts) (follows? prems concl (apply rule ts)))
(cartesian-exponent targets (procedure-arity rule))))
2025-02-20 23:11:31 -05:00
(call/1cc
(lambda (return)
(for-each
(lambda (step)
2025-02-21 00:24:13 -05:00
(define concl (step-conclusion step))
(define rule (step-rule step))
(define ok?
(if (procedure? rule)
(handle-general-step concl rule)
(follows? prems concl rule)))
(if ok?
(set! prems (cons concl prems))
(begin
(fail concl (if (procedure? rule) (placeholder-saturate rule) rule))
(return #f))))
2025-02-20 23:11:31 -05:00
(proof-steps proof))
2025-02-20 23:38:00 -05:00
(when (or (member #f prems) (premises<=? goals prems))
2025-02-20 23:11:31 -05:00
(return #t))
2025-02-21 00:24:13 -05:00
(format #t "Unsolved goals:\n~{~a\n~}~{⊢ ~a\n~}\n"
prems (filter (lambda (g) (not (member g prems))) goals)))))
2025-02-20 23:11:31 -05:00
;; TODO: i can just guess lol
2025-02-20 23:38:00 -05:00
;; this really isn't far from the macroless version....
2025-02-20 23:11:31 -05:00
(define and-comm
(proof (p q)
(argument (p q)
[(pand p q)]
[(pand q p)])
[p (simplification p q)]
[q (simplification p q)]
[(pand q p) (conjunction q p)]))
2025-02-20 22:26:09 -05:00
2025-02-21 00:24:13 -05:00
(define and-comm-ez
(proof (p q)
(argument (p q)
[(pand p q)]
[(pand q p)])
[p simplification]
[q simplification]
[(pand q p) conjunction]))