packages feed

haskeem-0.7.9: choice.scm

; Copyright 2009 Uwe Hollerbach <uh@alumni.caltech.edu>
; $Id: choice.scm,v 1.2 2009-07-11 04:32:54 uwe Exp $
; BSD3

; Non-deterministic programming: my version of (amb) with slightly different
; names, except this is more limited in that I can't call (fail) arbitrarily
; far into the future.

; Still TODO: make it work with streams as well as lists: that means that in
; the call to (,loop (cdr ,chs)) near the bottom of the macro, we might need
; to force a promise. ----> TODO^2: need to be able to query if something is
; a promise: write (promise?) primitive

; With streams, there is the issue that an infinite stream locks in all the
; previous choices, since this stuff does things in order from innermost to
; outermost: if an inner set of alternatives is infinite, it'll take infinite
; time to explore it all.

; Stack of escapers for the different choices

(define *choice-stack* (make-stack))
(define *choice-fail* (gensym))

; This function rejects the current set of choices: it pops the top
; escaper off the stack, then calls it with the failure symbol as its
; arg.  The failure symbol is gensym'ed, so it can't be generated by
; the user; so there is no worry about collisions with something the
; user might want to return.

(define (bad-choice)
  (if (*choice-stack* 'empty?)
      (raise "All out of choices!")
      ((*choice-stack* 'pop) *choice-fail*)))

; This macro does the binding of the variable sym to one of the choices given,
; then executes the body. If the body calls (bad-choice), this loops to the
; next choice. If the current set of choices is exhausted, the next-lower
; choice gets reset, and so on, until we run out of choices, in which case
; the (bad-choice) function raises an(other) exception.

(defmacro (let-choice sym choices . body)
  (let ((ret (gensym))
	(cont (gensym))
	(chs (gensym))
	(loop (gensym)))
    `(letrec* ((,loop (lambda (,chs)
			(if (null? ,chs)
			    (bad-choice)
			    (let* ((,sym (car ,chs))
				   (,ret (let/cc1 ,cont
					   (*choice-stack* 'push ,cont)
					   ,@body)))
			      (if (eqv? ,ret *choice-fail*)
				  (,loop (cdr ,chs))
				  (begin (*choice-stack* 'pop)
					 ,ret)))))))
	      (,loop ,choices))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Some simple examples

(define (require pred?) (if (not pred?) (bad-choice)))

(define (parlor-trick n)
  (let-choice j '(1 2 3 4 5)
    (let-choice i '(1 2 3 4 5)
      (require (= (+ i j) n))
      (list i j))))

(display (parlor-trick 4))
(newline)

(display (parlor-trick 6))
(newline)

(display (parlor-trick 10))
(newline)

(display (parlor-trick 11))
(newline)