[plt-scheme] a couple of questions

From: Gregory Woodhouse (gregory.woodhouse at sbcglobal.net)
Date: Sat Jan 20 20:08:03 EST 2007

On Jan 20, 2007, at 2:20 PM, Danny Yoo wrote:

>
>
> On Fri, 12 Jan 2007, Gregory Woodhouse wrote:
>
>> I'm trying to write an evaluator in the style of SICP that I hope  
>> will be at least somewhat idiomatic.
>
> Hi Greg,
>
>
> Did you get any responses about this yet?


Actually, i thought the question hadn't made it to the list, but in a  
response to a separate posting, Matthew Flatt did show me how to use  
exn-message to display the message associated with the user exception:

;; safe-eval-strict: expression environment -> value
   (define (safe-eval-strict input-val evn)
     (with-handlers
         ((exn:fail:user? (lambda (exn)
                            ((error-display-handler)
                             (exn-message exn)
                             exn))))
       (eval-strict input-val evn)))


I don't know how I missed it, because I had been searching for a way  
to display that message. I was able to find information on the error  
display handler, but that was the missing piece.


> Let's take a look.
>
> ... ok.  You may be able to avoid mutation here.  Let's look at a  
> portion of the code:
>
>      (let loop ()
>        (with-handlers ...
>          (printf "~a " main-prompt)
>          (set! input-val (read))
>          ...))
>
>
> Here is a let loop here with no variables being bound.  Every time  
> through the loop, though, we're rebinding input-val.  This idea can  
> be captured by making the input-val a part of the loop's variables:
>
>      (let loop ([input-val (begin (printf "~a " main-prompt)
>                                   (read))])
>        (with-handlers (...)
>          ...))

Yes, I ws trying to get the sequencing right and if I had only  
factored out read prompt, as you do below, then this would have been  
more obvious (I hope!)

>
> Since we're going to be doing prompting quite a bit, we can then  
> pull that prompting code up as a separate function.  I'll just call  
> it READ-PROMPT for now:
>
>   (define (read-prompt)
>     (printf "~a " main-prompt)
>     (read))
>

Now, WHY I didn't do this...well, that's a mystery!

>
> After this, the program looks a little bit more like:
> ...


>> 1. I don't want to break out of the loop when evaluating an  
>> expression like (/ 1 0) but instead report the error and then give  
>> the user an opportunity to enter another expression, leaving the  
>> environment intact. But, of course, I do want to break out of the  
>> loop in case of a programming error on my part, so my handler for  
>> exn:fail:user includes (loop) (where loop is the label in named  
>> let). I've never encountered code like this, and I wonder if it's  
>> even safe, much less idiomatic scheme.
>
> It looks ok to me.  I think it can be better, though.  You could  
> probably push the exception handling closer to the expression  
> that's likely to cause havoc, the eval-strict function.

Yes, I think I realized that after my next iteration, but getting the  
syntax right was a challenge.

>
> Let's wrap eval-strict with a helper function SAFE-EVAL-STRICT,  
> which we'll introduce within the LOOP definition.
>
> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
>     (let loop ([input-val (read-prompt)])
>       (define (safe-eval-strict input-val evn)
>         (with-handlers
>             ((exn:fail:user? (lambda (exn) (printf "Error!~n")
>                                (loop (read-prompt)))))
>           (eval-strict input-val env)))
>       ...)
> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
> ....

Ah! I'll bet that I didn't think of this because read-prompt is a  
function.

> ...
>
>> 2. The second question involves the environment, which I  
>> anticipate storing as a list or perhaps a hash table.  To me, it  
>> makes more sense for the evaluator to return an updated  
>> environment as a value than to have it modify a global value.
>
> Yes.  Also, just as in the case with INPUT-VAL, the environment is  
> a value that's probably going to follow along in the looping: it  
> might be appropriate to pass that around as well.  The structure of  
> RUN can turn into:
>
> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
> ;;
>   (define (run)
>     (printf "~a (version ~a)~n" evaluator-name evaluator-version)
>     (let loop ([input-val (read-prompt)]
>                [env '()])
>       ...))
> ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
> ;;

What I had been trying to do is maintain a kind of a "global" (or  
maybe top-level) environment, so that the user could type in a series  
of (define....) statements, so I don't really want a fresh  
environment for every evaluation. But this may be overloading the  
term environment a bit, because when evaluating an expression, you  
really do want a fresh environment.

>
> And now LOOP is actually starting to look a little bit like, well...
>
> http://download.plt-scheme.org/doc/360/html/r5rs/r5rs-Z-H-9.html#% 
> _idx_600
>
>

Hmm...This link doesn't work for me, but I'm sure I can chase it down.

>
>> But it also seems that this style could involve a lot of  
>> unnecessary copying.
>
> It really depends on how you define environments and how you extend  
> them. It's possible to define them so that you take advantage of  
> shared structure.  That is, rather than a mutable hashtable, you  
> can choose a representation that reuses the existing environment  
> structure.

Actually, I started thinking about this after reading the first two  
sections of ch. 4 of SICP. That being said, my impression was that  
the environments in SICP were basically auxiliary data structures  
used during the evaluation of a single expression and then discarded.  
Maybe I need to take another look.

Actually, there are two other reasons why I'm interested in avoiding  
copying -- One is that a potential application I have in mind  
involves potentially huge numbers of bindings being "in effect" (tens  
of thousands), a small subset of which may actually be uzed in any  
one computation. The other is that I'm thinking about sharing some of  
these bindings between different threads. (Yes, I know that doesn't  
sound very functional!)  To be a bit less mysterious about it, I've  
been working for some years on a MUMPS based medical information  
system known as VistA.  I've been unhappy about the lack of clear  
semantics for the system, but then I re(?)-discovered Scheme by  
picking up a copy of "The Little Schemer", and I was, well, hooked.


As always, thank you for your help!

Gregory Woodhouse
gregory.woodhouse at sbcglobal.net

There's a fine line between optimization
and not being stupid. -- R. Kent Dybvig



-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20070120/cdc702b9/attachment.html>

Posted on the users mailing list.