amb: Ambiguous Operator
1 Ambiguous Operator
syntax
(amb expr ...)
The form (amb expr ...) expands to (amb* (λ () expr) ...).
Wrapping amb expressions with a new amb sequence is recommended. This ensures that each instance of non-deterministic computation starts with a fresh sequence, avoiding unintended interactions between different amb expressions.
> (parameterize ([current-amb-tasks ((current-amb-maker))]) (let ([x (amb 1 2)]) (displayln (list x)) (let ([y (amb 3 4)]) (displayln (list x y)) (let ([z (amb 5 6)]) (displayln (list x y z)) (amb)))))
(1)
(1 3)
(1 3 5)
(1 3 6)
(1 4)
(1 4 5)
(1 4 6)
(2)
(2 3)
(2 3 5)
(2 3 6)
(2 4)
(2 4 5)
(2 4 6)
amb: empty amb tasks;
expected at least one amb task
in: (amb)
> (parameterize ([current-amb-shuffler void] [current-amb-tasks ((current-amb-maker))] [current-amb-pusher enqueue!]) (let ([x (amb 1 2)]) (displayln (list x)) (let ([y (amb 3 4)]) (displayln (list x y)) (let ([z (amb 5 6)]) (displayln (list x y z)) (amb)))))
(1)
(2)
(1 3)
(1 4)
(2 3)
(2 4)
(1 3 5)
(1 3 6)
(1 4 5)
(1 4 6)
(2 3 5)
(2 3 6)
(2 4 5)
(2 4 6)
amb: empty amb tasks;
expected at least one amb task
in: (amb)
syntax
(for/amb maybe-length (for-clause ...) break-clause ... body ...+)
(for/amb type-ann-maybe maybe-length (for-clause ...) type-ann-maybe expr ...+)
syntax
(for*/amb maybe-length (for-clause ...) break-clause ... body ...+)
(for*/amb type-ann-maybe maybe-length (for-clause ...) type-ann-maybe expr ...+)
2 Stream Constructor
syntax
(in-amb expr)
The in-amb form automatically creates a new amb sequence and records all relevant parameters at the time expr is evaluated, so there is no need to worry about affecting calls to other amb expressions.
The form (in-amb expr) expands to (in-amb* (λ () expr)).
> (parameterize ([current-amb-tasks ((current-amb-maker))]) (define (next i j) (amb (values i j) (next (add1 i) (sub1 j)))) (amb 1 2 3) (displayln (= 2 ((current-amb-length) (current-amb-tasks)))) (time (displayln (for/and ([i (in-range 1000000)] [(j k) (in-amb (next 0 0))]) (= i j (- k))))) (displayln (= 2 ((current-amb-length) (current-amb-tasks)))))
#t
#t
cpu time: 2450 real time: 2451 gc time: 79
#t
A good practice is to wrap amb expressions in a procedure, then use in-amb or in-amb* to create a lazy stream that produces as many results as needed.
> (define (f n) (define m (amb 0 1 2 3 4)) (unless (> m n) (amb)) m)
> (for ([m (in-amb (f 2))]) (displayln m))
3
4
> (define (thk) (define x (for/amb ([i 10]) i)) (unless (even? x) (amb)) x) > (for/list ([x (in-amb* thk)]) x) '(0 2 4 6 8)
3 Exception Type
struct
(struct exn:fail:contract:amb exn:fail:contract () #:extra-constructor-name make-exn:fail:contract:amb #:transparent)
> (parameterize ([current-amb-tasks ((current-amb-maker))]) (amb)) amb: empty amb tasks;
expected at least one amb task
in: (amb)
> (parameterize ([current-amb-tasks ((current-amb-maker))]) (amb*)) amb: empty amb tasks;
expected at least one amb task
in: (amb)
> (parameterize ([current-amb-tasks ((current-amb-maker))]) (for/amb ([i '()]) i)) amb: empty amb tasks;
expected at least one amb task
in: (amb)
> (parameterize ([current-amb-tasks ((current-amb-maker))]) (for*/amb ([i '()]) i)) amb: empty amb tasks;
expected at least one amb task
in: (amb)
procedure
> (raise-amb-error) amb: empty amb tasks;
expected at least one amb task
in: (amb)
4 Parameter
parameter
(current-amb-empty-handler empty-handler) → void? empty-handler : (-> none/c)
parameter
(current-amb-shuffler shuffle!) → void? shuffle! : (-> mutable-vector? void?)
parameter
(current-amb-maker) → (-> sequence?)
(current-amb-maker make) → void? make : (-> sequence?)
parameter
(current-amb-tasks tasks) → void? tasks : sequence?
parameter
→ (-> sequence? exact-nonnegative-integer?) (current-amb-length length) → void? length : (-> sequence? exact-nonnegative-integer?)
parameter
(current-amb-popper) → (-> sequence? (-> none/c))
(current-amb-popper pop!) → void? pop! : (-> sequence? (-> none/c))