[plt-scheme] Re: plt-scheme digest, Vol 1 #793 - 11 msgs

From: Joe Marshall (jrm at ccs.neu.edu)
Date: Thu Apr 22 09:31:36 EDT 2004

Bill Richter <richter at math.northwestern.edu> writes:

> A procedure call (foo goo) where the side-effects of foo affect goo.
> In R5RS Scheme, that's a bug, because you don't know the eval order.
>
> In Mzscheme, that's "formerly-buggy", because Mzscheme specifies
> left->right evaluation.  We know that foo is gonna get evaluated
> first, and so we can count on that, and know what the effect on goo's
> evaluation is gonna be.

There is a subtle flaw in your logic here.  If you *know* that foo
will have an effect on the evaluation of goo in the second example,
you should also *know* that in the first example.  Therefore, you
*wouldn't* write the first example in that way, you'd write something
more like 

  (let* ((proc (foo))
         (arg  (goo)))
     (proc arg))

> Right, but if they change the language, they change some bugs.  My
> (foo goo) is always buggy in R5RS Scheme, but it's not a bug in
> Mzscheme if we're deliberately taking advantage of a feature.

Again, there is a flaw in the argument.  Since 

  (let* ((proc (foo))
         (arg  (goo)))
     (proc arg))

is always correct no matter what the order of evaluation is, and since
it clearly notifies the casual observer that (foo) *must* be evaluated
before (goo), there is no advantage to be gained by deliberately
writing:

  ((foo) (goo))

A `feature' that does not confer an `advantage' is not worthy of the
name.

> Bradd (I think) wrote:
>    Sequentiality (or lack of it) is an important design decision. 

To tell the truth, the primary reason that order of evaluation is not
specified is because MIT did it right to left and Indiana did it left
to right and there was no compelling argument that could sway either
side.


> Bill Richter wrote:
>
> Yeah, if we don't know what we're doing!  

That is normally the case, is it not?

> Knowing about left->right eval, I don't see the difference between
>
> (foo goo)
>
> and 
>
> (let ([eval-function-1st foo])
>   (let ([eval-argument-2nd goo])
>     (eval-function-1st eval-argument-2nd)))
>
> I mean, the 2nd is driving home the point that we really want to
> evaluate foo first.   

That is crucial difference!  Yes, the resulting actions of the
computer might be identical, but the impression that it has on the
next person reading the code is quite different.

> But in Mzscheme, you don't have to, and it's "the same" as 
> (foo goo).

It may be the same to the computer, but it is *not* the same to me.

-----

Incidentally, I'd prefer it if the function position were 
evaluated *last*.  When I'm developing code, I often write function
calls to functions that do not yet exist:

  (define (frob my-object)
    (call-with-complicated-setup
      (lambda ()
        (display "debug:  about to frob")
        (frob-kernel (prepare-object my-object)))))

  (define (prepare-object object)
    (if (and (pair? object)
             (number? (cdr object)))
        (cons (car object) (1+ (cdr object)))
        (error "Bad object")))

Now I want to test my code:
 (frob (make-some-object))

> Error:  undefined variable FROB-KERNEL

Well, I *know* that.  I haven't written it yet.  I wanted to see if
the argument to FROB-KERNEL was properly prepared.  If the function
position were evaluated last, I'd have gotten through the call to
prepare-object.  So now I have to write a stub:

(define (frob-kernel . args) 
  (display args) 
  (error "Time to write frob-kernel"))

But I absolutely *hate* doing this.  It reminds me too much of those
anal languages where everything has to be written and type checked
before you can test any part of the program.

With MIT-Scheme's right->left order of evaluation, I can enter the
debugger just before the call to FROB-KERNEL, look at the arguments to
ensure they are what I expect, and without restarting from scratch
define the frob-kernel function and restart the call.

        




Posted on the users mailing list.