10 Ambiguousness
If we’ve been through the previous part, we can keep using the same file. Or we can use 6-booleans.rkt as our starting point.
10.1 Some tests
(check-equal? (eval-require primitives (λ (x f) #t) (λ () #f) '(< 3 6)) #t)
(check-equal? (eval-require primitives (λ (x f) #t) (λ () #f) '(> 3 6)) #f)
(check-equal? (evaluate '(begin (define a (amb 1 (- 5 3) 6 8)) (require (> a 5)) a)) 6)
(check-equal? (evaluate '(begin (define a (amb 1 3 5 7)) (define b (amb 2 4 3 6)) (require (= (+ a b) 9)) (list a b))) '(3 6))
(check-equal? (evaluate* '(begin (define a (amb 1 (- 5 3) 6 8)) (require (> a 5)) a)) '(6 8))
(check-equal? (evaluate* '(begin (define a (amb 1 3 5 7)) (define b (amb 2 4 3 6)) (require (= (+ a b) 9)) (list a b))) '((3 6) (5 4) (7 2)))
10.2 Some stuff will have a fail-parameter
The functions we use as continue-continuations, as well as all functions that have continue-parameters, should also have fail-parameters. +Like, we will change continuation-lambdas like (λ (value) stuff) to ones like (λ (fail value) stuff), +and things like (eval-exp env continue exp) to things like (eval-exp env continue fail exp). +For now, the different functions will just pass their fails along.
(define (evaluate input) (eval-exp primitives (λ (fail res) res) (λ () (error 'ohno)) input))
10.3 require
[(list 'require x) (eval-require env continue fail x)]
The continuation we pass along to eval-exp should carry on with continue if x evaluated to true, +or use fail if it evaluated to false. +The fail-continuation should not take any arguments.
The two tests that use eval-require should pass after this.
10.4 amb
To eval-exp we add:
[(list 'amb exps ...) (eval-amb env continue fail exps)]
And we make the function (eval-amb env continue fail exps).
10.5 Btw let’s add a list-function to our primitives
Just, it would be nice to return multiple values now, so we will add Racket’s list-function to the primitives list.
10.6 evaluate*
So evaluate should work mostly like before, and it will return the first solution, or throw an error if there aren’t any.
It would be neat to have an evaluation-function that could return a list of solutions instead. So we will make one. +In evaluate*, we, uh, “replace failure with a list of successes,” maybe:
(define (evaluate* input) (eval-exp primitives (λ (fail res) (cons res (fail))) (λ () '()) input))
Now we can, say, make a program for finding numbers that add up to 10:
(define adds-up-to-10 '(begin (define a (amb 1 2 3 4 5 6 7 8 9)) (define b (amb 1 2 3 4 5 6 7 8 9)) (require (= (+ a b) 10)) (list a b)))
And we can get one solution with (evaluate adds-up-to-10), or several solutions with (evaluate* adds-up-to-10).
10.7 Done?
Run and see that all the tests pass.
Next: Maybe It’s puzzle time, or we can go Towards zebras. We should be equipped for either. +We can keep using the Racket-file we’re working with, or skip to 7-amb.rkt..