I have a question about Exercise 4.54 from Section 4.3.3 of Structure and Interpretation of Computer Programs (http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-28.html#%_sec_4.3.3). This exercise concerns the Amb evaluator.
The exercise is the following:
If we had not realized that require could be implemented as an ordinary procedure that uses amb, to be defined by the user as part of a nondeterministic program, we would have had to implement it as a special form. This would require syntax procedures
(define (require? exp) (tagged-list? exp 'require))
(define (require-predicate exp) (cadr exp))
and a new clause in the dispatch in analyze
((require? exp) (analyze-require exp))
as well the procedure analyze-require that handles require expressions. Complete the following definition of analyze-require.
(define (analyze-require exp)
(let ((pproc (analyze (require-predicate exp))))
(lambda (env succeed fail)
(pproc env
(lambda (pred-value fail2)
(if <??>
<??>
(succeed 'ok fail2)))
fail))))
I completed it as follows:
(define (analyze-require exp)
(let ((pproc (analyze (require-predicate exp))))
(lambda (env succeed fail)
(pproc env
(lambda (pred-value fail2)
(if (false? pred-value)
(fail2) ;; or (fail)
(succeed 'ok fail2)))
fail))))
My doubt is the following:
I know that, during execution, when the predicate value pred-value is false, require should fail; that is, it should call a failure continuation procedure. But I'm a bit confused as to whether it should call (fail) or (fail2). Which one is correct?
(fail2) is the correct one. The procedure conforms to a continuation-passing style, and the correct continuation procedure in this case is fail2.
I think (fail) is correct.
Here we have got pred-value, which means pproc evaluates well for now. But if it is false value, we treat it as bad as pproc evaluates wrong. so the continuation here is fail not fail2.
A test can be found here:
https://wizardbook.wordpress.com/2011/01/22/exercise-4-54/
(fail2)is correct, well,most of the time it doesn't matter whichever you choose,but for the following conditons:
1.the predicate part contains (amb) such as
(require (amb false true false true true))
2.the predicate part contains (set! <...> <...>)
you can check it out,this is because most of the process's fail-continuation is the same as its caller's except for the above two kinds of processes. Though it sounds some kind of ridiculous.
Related
Update: The original version of this question did not fully describe the constraints of my situation. It now includes an example with some hand-waving for the sake of simplicity as well as a testable minimum example.
I am trying to pass a snippet of code into a procedure for evaluation at a later point in time. The system with all of the constraints looks like this:
; External Library code - cannot be changed
(define (serialize-parameters . args)
(format some-file "~A~%" args))
(define (function-called-later-possibly-after-a-system-reboot callback)
(apply callback some-default-parameters (deserialize-parameters some-file)))
; Internal library code - can be changed, but the value of `code-snippet` cannot be hardcoded
(define (callback some-default-parameters code-snippet)
(eval code-snippet (current-module))
; Application code - can be changed, can be hardcoded
(define (main)
(serialize-parameters #:code-snippet '(format #t "~A~%" some-default-parameters)))
The main point is to allow the application to execute an arbitrary code snippet in a callback function. The callback function receives other parameters which are not known to the application and may change between calls.
An important constraint is that the parameters that the application wants to send to the callback are written to a file and then read back at a later point in time, possibly after a system reboot.
This example is easier to test, but does not capture all of the constraints:
(define (test foo bar)
(eval bar (current-module)))
(test "value of foo" '(format #t "~A~%" foo))
Running this program results in Unbound variable: foo. What I want is for the test function to be defined in such a way that the result of the call will be "value of foo\n" being printed to the terminal. Is this possible in Guile?
Thank you.
This will no work. eval takes an environment and you pass it (current-module). That are top level bindings in the module, like test but not any lexical bindings like foo or bar. They just don't exist in the environment returned by (current-module).
You can do this:
(define foo "value of foo")
(eval '(format #t "~A~%" foo) (current-module))
; prints "value of foo"
; ==> #t
Also, the elephant in the room is that you can do this with lambdas:
(define (test value proc)
(proc value))
(test "value to process" (lambda (foo) (format #t "~A~%" foo)))
; prints "value to process"
; ==> #t
Alternatively, but I'm guessing you can't have format in callback because "code-snippet" can have many different values:
(define (main)
(serialize-parameters #:code-snippet "~A~%"))
(define (callback some-default-parameters code-snippet)
(format #t code-snippet some-default-parameters))
EDIT
I think you can do it semi hard-coded:
(define (main)
(serialize-parameters #:code-snippet 'print-format))
(define (callback argument message)
(case message
((print-format) (format #t "~A~%" argument))
((other-message) (handle-message ...))
(else ...)))
You can even make it a dynamic dispatcher. Eg. you do something like this:
(define messages '())
(define (register-callback message proc)
(set! messages (cons (cons message proc) messages)))
(define (callback argument message)
(let ((found (assq message messages)))
(when found
((cdr found) argument))))
(register-callback 'print-format (lambda (arg) (format #t "~A~%" arg)))
(callback "test" 'print-format) ; prints "test"
Now only the message gets stored in a file, which easily can be any data literal.
In miniKanren, succeed can be defined as (define succeed (== #t #t)), and fail can be defined as (define fail (=== #t #f)). But what about #s and #u as short forms of succeed and fail, as they appear in The Reasoned Schemer?
(define #s succeed) produces an error in Racket:
Welcome to Racket v7.2.
> (require Racket-miniKanren/miniKanren/mk)
> (define #s succeed)
; readline-input:2:8: read-syntax: expected `(`, `[`, or `{` after `#s` [,bt
; for context]
#<procedure:...iniKanren/mk.rkt:337:4>
; readline-input:2:18: read-syntax: unexpected `)` [,bt for context]
I have the feeling that this has something to do with reader macros.
How can I define #s for succeed and #u for fail in Scheme and also in Racket?
I am using the canonical miniKanren implementation for Scheme and the canonical miniKanren implementation for Racket.
Identifiers in Racket can not begin with #. It is simple to bind the identifiers s and u. Redefining the meaning of #s and #u is not as simple, since it needs to happen in the reader. Normally #something signals to reader that something special is to be read.
The input (foo bar) will be read as a list, #(foo bar) will be read as a vector, and #s(foo bar) will be read as a structure. You can read about the standard syntax here:
https://docs.racket-lang.org/reference/reader.html?q=%23s#%28mod-path._reader%29
Now if you want to change the meaning of #s and #u you need to look at readtables.
Each time the reader sees an # it consults a readtable to see how to handle the following characters. Since reading happens before parsing/expansion and evaluation, you can't change the reader simply by calling a function in your program. You will need to either use
the #reader extension mechanism or create your own language.
For more on readtables: https://docs.racket-lang.org/reference/readtables.html?q=reader-macro
The Guide has an example of how to use reader extensions:
https://docs.racket-lang.org/guide/hash-reader.html
I solved all the book using
(define succeed
(lambda (s)
`(,s)))
(define SUCC succeed)
(define fail
(lambda (s)
'()))
On the other side, you should consult the source code provided by Friedman & Byrd. I solved it using mit-scheme -- no specific feature of racket is used, R6RS is enough.
For Racket, #s and #u can be defined as such (reference: Using Racket for The Reasoned Schemer):
;; #s for succeed.
(current-readtable
(make-readtable (current-readtable)
#\s
'dispatch-macro
(lambda (ch port src line col pos) succeed)))
;; #u for fail.
(current-readtable
(make-readtable (current-readtable)
#\u
'dispatch-macro
(lambda (ch port src line col pos) fail)))
Note that this only works in the REPL.
This defines #s and #u by modifying the readtable.
For Scheme, adding read syntax is defined in SRFI-10 sharp-comma external form, but the resulting #,() forms are probably awkward for most tastes. For Scheme, it is best to just define s and u because there is currently no portable way to define #s and #u.
(define bootstrap-c-code
(define (from-file file-name)
(let* ((ip (open-input-file file-name))
(res (read-text-file-from-input-port ip)))
(close-input-port ip)
res))
(from-file "llvm.c"))
Error : define: bad syntax (multiple expressions after identifier)
But I can't see anything wrong with it. Can someone explain / fix it please.
It's not clear what you intended with the above code. If you were trying to load a text file and leave the loaded value in a variable called bootstrap-c-code, then try this:
(define bootstrap-c-code
(let ((from-file
(lambda (file-name)
(let* ((ip (open-input-file file-name))
(res (read-text-file-from-input-port ip)))
(close-input-port ip)
res))))
(from-file "llvm.c")))
Of course, the from-file definition will only be visible inside the let, if you need to use it outside, define it outside of the whole expression. If you only need the functionality of from-file inside the let, you can obtain the same result in a much simpler way:
(define bootstrap-c-code
(let* ((ip (open-input-file "llvm.c"))
(res (read-text-file-from-input-port ip)))
(close-input-port ip)
res))
On the other hand, if what you intended was to create a procedure called bootstrap-c-code, then the correct syntax would be:
(define (bootstrap-c-code)
(define (from-file file-name)
(let* ((ip (open-input-file file-name))
(res (read-text-file-from-input-port ip)))
(close-input-port ip)
res))
(from-file "llvm.c"))
According to R5RS, internal definitions can occur only at the beginning of the of a bunch of forms like let, let*, lambda etc. In the case of your code, that is not the case since you have an internal definition inside a non-procedural define. You could fix it by making `bootstrap-c-code' bind to a procedure.
In Exercise 35.4.2 from HtDP, I've implemented the GUI and have a button called "Remove" which invokes a callback function. Here it is:
(define (cb-remove x)
((lambda (name result)
(cond
[(number? result) (remove-name name address-book)]
[else (draw-message msg "Not found")]))
(string->symbol (text-contents label-name))
(lookup (string->symbol (text-contents label-name)) address-book)))
When I run this, I get the following message: button-callback: result of type <Boolean> expected, your function produced #<set!-result>. The problem is that I have to call set! in order to change the address book. However, the result of set! is (void), which cannot cannot be combined with a Boolean type. How can I avert this problem? Thanks for any insight.
Simple:
(begin (set! foo bar) #t)
(defspel game-action (command subj obj place &rest rest)
`(defspel ,command (subject object)
`(cond ((and (eq *location* ',',place)
(eq ',subject ',',subj)
(eq ',object ',',obj)
(have ',',subj))
,#',rest)
(t '(i cant ,',command like that.)))))
Thats the code from http://www.lisperati.com/actions.html for the 'macro defining macro'. I can't seem to convert it appropriately to scheme. Can someone explain to me the process of creating this same sort of thing in Scheme?
This kind of macro is actually much simpler in Scheme, since you can do it all with define-syntax-rule (in standard Scheme code you will need define-syntax + syntax-rules). You basically do the same thing, minus the whole quote/unquote mess.
(defspel (game-action command subj obj place rest ...)
(defspel (command subject object)
(cond [(and (eq? *location* 'place)
(eq? 'subject 'subj)
(eq? 'object 'obj)
(have 'subj))
rest ...]
[else '(i cant command like that.)])))
And since this is actually most of the code, I ported the whole thing to PLT -- see the post on the mailing list.