amb: Ambiguous Operator
1 Ambiguous Operator
syntax
(amb expr ...)
The form (amb expr ...) expands to (amb* (λ () expr) ...).
> (amb 1 (values 2 3) 4) 1
> (amb)
2
3
> (amb) 4
> (amb) amb: empty amb tasks;
expected at least one amb task
in: (amb)
For most use cases, we recommend using in-amb to construct a stream from ambiguous computations.
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.
> (define make-amb-tasks (current-amb-maker)) > (amb 1 (values 2 3) 4) 1
> (parameterize ([current-amb-tasks (make-amb-tasks)]) (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)
> (amb)
2
3
> (parameterize ([current-amb-shuffler void] [current-amb-tasks (make-amb-tasks)] [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)
> (amb) 4
> (amb) 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 ...+)
> (for/amb #:length 4 ([i #(1 2)] [j #(x y)]) (values i j))
1
'x
> (amb)
2
'y
> (amb) 0
> (amb) 0
> (amb) amb: empty amb tasks;
expected at least one amb task
in: (amb)
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)).
> (amb 1 2 3) 1
> (for/list ([(i j) (in-amb (amb (values 1 'x) (values 2 'y)))]) (cons i j)) '((1 . x) (2 . y))
> (amb) 2
> (amb) 3
> (amb) amb: empty amb tasks;
expected at least one amb task
in: (amb)
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/list ([m (in-amb (f 2))]) 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))