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
You might want to copy them into the test module at the bottom of your Racket file. The tests are going to fail at first. By the end of this chapter they should pass.
(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-exn exn:fail? (λ() (evaluate '(begin (define a (amb 1 2 3)) (require (> a 5)) a))))
(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 exp) (eval-require env continue fail exp)]
The continuation we pass along to eval-exp should carry on with continue if exp 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..