(define *lc-cells* 0)
(define (lc-reset!) (set! *lc-cells* 0))
(define (lc-count) *lc-cells*)
(define (lc-cons! v tail)
(set! *lc-cells* (+ *lc-cells* 1))
(cons v tail))
(define (lc-map f xs)
(if (null? xs) '()
(lc-cons! (f (car xs)) (lc-map f (cdr xs)))))
(define (lc-filter p xs)
(cond ((null? xs) '())
((p (car xs)) (lc-cons! (car xs) (lc-filter p (cdr xs))))
(else (lc-filter p (cdr xs)))))
(define (stage-run stage xs)
(cond ((equal? (car stage) 'map) (lc-map (cadr stage) xs))
((equal? (car stage) 'filter) (lc-filter (cadr stage) xs))
(else (error (string-append "lc: unknown stage "
(format "~a" (car stage)))))))
(define (naive-run pipeline xs)
(if (null? pipeline) xs
(naive-run (cdr pipeline) (stage-run (car pipeline) xs))))
(define (fuse-elem pipeline x) (let loop ((ps pipeline) (v x))
(if (null? ps) (list 'keep v)
(let ((stage (car ps)))
(cond ((equal? (car stage) 'map)
(loop (cdr ps) ((cadr stage) v)))
((equal? (car stage) 'filter)
(if ((cadr stage) v) (loop (cdr ps) v) (list 'drop)))
(else (error "lc: unknown stage")))))))
(define (fused-run pipeline xs)
(if (null? xs) '()
(let ((r (fuse-elem pipeline (car xs))))
(if (equal? (car r) 'keep)
(lc-cons! (cadr r) (fused-run pipeline (cdr xs)))
(fused-run pipeline (cdr xs))))))
(define (run pipeline xs) (fused-run pipeline xs))
(define (lc-guard-expr gs) (if (null? gs) #t (cons 'and gs)))
(defmacro lc (yield binding . guards)
(let ((var (car binding))
(arrow (cadr binding))
(src (caddr binding)))
(if (not (equal? arrow '<-))
(error "lc: binding must be (var <- source)")
`(fused-run
(list (list 'filter (lambda (,var) ,(lc-guard-expr guards)))
(list 'map (lambda (,var) ,yield)))
,src))))
(define (lists-of-len alphabet n)
(if (= n 0) (list '())
(let ((shorter (lists-of-len alphabet (- n 1))))
(apply append
(map (lambda (a) (map (lambda (t) (cons a t)) shorter)) alphabet)))))
(define (lists-upto alphabet maxlen)
(apply append
(map (lambda (n) (lists-of-len alphabet n)) (range 0 (+ maxlen 1)))))
(define (verify-fusion pipeline domain)
(check-exhaustive
(lambda (xs) (equal? (naive-run pipeline xs) (fused-run pipeline xs)))
(list domain)))
(define (comp2 f g) (lambda (x) (f (g x))))
(define (verify-map-map-law g f domain)
(check-exhaustive
(lambda (xs)
(equal? (naive-run (list (list 'map g) (list 'map f)) xs)
(naive-run (list (list 'map (comp2 f g))) xs)))
(list domain)))
(define (verify-filter-filter-law p q domain)
(check-exhaustive
(lambda (xs)
(equal? (naive-run (list (list 'filter p) (list 'filter q)) xs)
(naive-run (list (list 'filter (lambda (x) (and (p x) (q x))))) xs)))
(list domain)))
(define (naive-sum-map f xs) (sum (lc-map f xs))) (define (fused-sum-map f xs) (let loop ((ys xs) (acc 0))
(if (null? ys) acc (loop (cdr ys) (+ acc (f (car ys)))))))
(define (verify-sum-map-fusion f domain)
(check-exhaustive
(lambda (xs) (= (naive-sum-map f xs) (fused-sum-map f xs)))
(list domain)))
(define (verify-reorder-map-filter f p domain)
(check-exhaustive
(lambda (xs)
(equal? (naive-run (list (list 'map f) (list 'filter p)) xs)
(naive-run (list (list 'filter p) (list 'map f)) xs)))
(list domain)))