The fold function captures a common recursion pattern
(define (fold kons knil list) ;; Uncurried version
(if (null? list)
knil
(kons (car list) (fold kons knil (cdr list)))))
so it can define a lot of common list functions easily:
(define (append x y) (fold cons y x))
(define (map f l) (fold (lambda (x xs) (cons (f x) xs)) '() l))
(define (reverse x) ((fold (lambda (x y) (lambda (z) (y (cons x z))))
(lambda (i) i)
x) '()))
Add a fold function to the relational interpreter:
....
((fresh (kons knil ls v3 r)
(== `(fold ,kons ,knil ,ls) expr)
(unboundo 'fold env)
(eval-expo ls env v3)
(make-foldo kons knil v3 r)
(eval-expo r env out)))
....
along with a helper relation to build a fold expression up:
(define (make-foldo kons knil ls res)
(conde
((== ls '()) (== res knil))
((fresh (u v m)
(== ls `(,u . ,v))
(== res `((,kons ',u) ,m))
(make-foldo kons knil v m)))))
Make relations to help run test queries:
(define (testo rec base input output)
(eval-expo `(fold ,rec ,base ',input) '() output))
(define (testo-app-nil rec base input output)
(fresh (t1 t2)
;; eta expanded template to help things along a little
(== base `(lambda (x) ,t1))
(== rec `(lambda (x) (lambda (y) (lambda (z) ,t2))))
(eval-expo `((fold ,rec ,base ',input) '()) '() output)))
and then we can derive some simple recursive functions as folds using tests!
(define (synth-appendo f)
(fresh (rec base)
(== f `(lambda (input) (fold ,rec ,base input)))
(testo rec base '(a b c) '(a b c u v))
(testo rec base '(x y) '(x y u v))
(testo rec base '() '(u v))))
;; => Yes. (361.12ms)
;; f: (lambda (input)
;; (fold (lambda (_.0)
;; (lambda (_.1)
;; (cons _.0 _.1)))
;; (quote (u v))
;; input))
(define (synth-map f)
(fresh (rec base)
(== f `(lambda (input) (fold ,rec ,base input)))
(testo rec base '() '())
(testo rec base '(x) '((f . x)))
(testo rec base '(y) '((f . y)))
(testo rec base '(u v w) '((f . u) (f . v) (f . w)))))
;; => Yes. (2284.32ms)
;; f: (lambda (input)
;; (fold (lambda (_.0)
;; (lambda (_.1)
;; (cons (cons (quote f) _.0) _.1)))
;; (quote ())
;; input))
(define (synth-reverse f)
(fresh (rec base)
(== f `(lambda (input) ((fold ,rec ,base input) '())))
(testo-app-nil rec base '() '())
(testo-app-nil rec base '(a) '(a))
(testo-app-nil rec base '(b) '(b))
(testo-app-nil rec base '(x y) '(y x))
(testo-app-nil rec base '(d e l i v e r) '(r e v i l e d))))
;; => Yes. (1277.19ms)
;; f: (lambda (input)
;; ((fold (lambda (x)
;; (lambda (y)
;; (lambda (z) (y (cons x z)))))
;; (lambda (x) x)
;; input)
;; (quote ())))
To derive the real append (rather than a specialized version):
(define (synth-appendo f)
(fresh (rec base)
(== f `(lambda (a)
(lambda (b)
(fold ,rec ,base a))))
(eval-expo `((,f '(a b c)) '(u v)) '() '(a b c u v))
(eval-expo `((,f '(x y)) '(z)) '() '(x y z))
(eval-expo `((,f '()) '(o k !)) '() '(o k !))))
;; => Yes. (461.12ms)
;; f: (lambda (a) (lambda (b) (fold (lambda (_.0) (lambda (_.1) (cons _.0 _.1))) b a)))