(module amb mzscheme
(provide with-amb amb-thunks amb amb-collect)
(require (lib "list.ss" "srfi" "1"))
(define (default-base-amb-fail)
(error 'amb "no more choices"))
(define base-amb-fail
(make-parameter default-base-amb-fail))
(define amb-running?
(make-parameter #f))
(define queue? (lambda (obj) (or (null? obj) (pair? obj))))
(define empty? null?)
(define head car)
(define pop cdr)
(define push cons)
(define (make-empty) '())
(define queue
(make-parameter (make-empty)))
(define-struct choice
(continuation thunk) #f)
(define (amb-fail)
(if (empty? (queue))
((base-amb-fail))
(let ((next-choice (head (queue))))
(queue (pop (queue)))
((choice-continuation next-choice)
((choice-thunk next-choice))))))
(define-syntax amb
(syntax-rules ()
((amb) (amb-fail))
((amb x) x)
((amb x y ...)
(call/cc
(lambda (k)
(amb-thunks k (lambda () x) (lambda () y) ...))))))
(define (amb-thunks k . thunks)
(if (amb-running?)
(begin (add-to-queue! k (cdr thunks))
(k ((car thunks))))
(error 'amb-thunks "no running amb instance")))
(define (add-to-queue! k thunks)
(let ((choices (map (lambda (thunk) (make-choice k thunk)) thunks)))
(queue (fold-right push (queue) choices))))
(define-syntax amb-collect
(syntax-rules ()
((amb-collect expr ...)
(let ((results '()))
(call/cc
(lambda (return)
(parameterize ((queue (make-empty))
(base-amb-fail (lambda () (return (reverse results))))
(amb-running? #t))
(let ((result (begin expr ...)))
(set! results (cons result results))
(amb)))))))))
(define-syntax with-amb
(syntax-rules ()
((with-amb expr ...)
(parameterize ((queue (make-empty))
(base-amb-fail default-base-amb-fail)
(amb-running? #t))
expr ...)))))