[racket] An example of let-vs-define: SICP 2.64

From: Neil Van Dyke (neil at neilvandyke.org)
Date: Fri Nov 2 18:19:32 EDT 2012

Here's a different comparison of let-vs-internal-define, using your code.

Your internal define example (hopefully whitespace doesn't get mangled 
by however you're reading this message):

(define (partial-tree elts n)
   (cond [(= n 0)
          (values EMPTY-TREE elts)]
         [else
          (define left-size (quotient (- n 1) 2))
          (define-values (left-tree non-left-elts)
            (partial-tree elts left-size))

          (define this-entry (first non-left-elts))

          (define right-size (- n (+ left-size 1)))
          (define-values (right-tree remaining-elts)
            (partial-tree (rest non-left-elts) right-size))

          (values (tree this-entry left-tree right-tree)
                  remaining-elts)]))

Simply transliterated to a let syntax, and with a more readable zero 
test (which you'd need a "begin" to do with internal define, incidentally):

(define (partial-tree elts n)
   (if (zero? n)
       (values empty-tree elts)
       (let* ((left-size                   (quotient (- n 1) 2))
              ((left-tree non-left-elts)   (partial-tree elts left-size))
              (this-entry                  (first non-left-elts))
              (right-size                  (- n (+ left-size 1)))
              ((right-tree remaining-elts) (partial-tree (rest 
non-left-elts)
                                                         right-size)))
         (values (tree this-entry left-tree right-tree)
                 remaining-elts))))

Which is better is subjective, but in the "let" example, I can instantly 
see that there is an ordered sequence of bindings, and what the names 
are, in a visually regular format.

In the define example, I have to first see that there's an extent 
consisting solely of a bunch of define forms in it with no non-define 
code hidden, then look for the names, then examine the code for ordering 
(e.g., are there recursive/forward references?), and tread carefully so 
that I don't get scrod by a toplevel-like idiosyncrasy.

This is a simple example.  I think let can sometimes win slightly more 
when the examples get more complicated.  (And there are other examples, 
such as with huge nested definitions, in which I sometimes think 
internal define might be more readable, but overall I usually find let 
to be more readable.)

In summary: You can make "let*-values" be unnecessarily awkward, and you 
can cherry-pick throwaway problem set code from SICP, and you can tell 
people they have to type ugly square brackets when they don't, but the 
people of Let will not be intimidated!  Long live Let! :)

Neil V.


Posted on the users mailing list.