1
(define (sqrt x)
  (define (good-enough? guess x)
    (< (abs (- (square guess) x)) 0.001))
  (define (improve guess x)
    (average guess (/ x guess)))
  (define (sqrt-iter guess x)
    (if (good-enough? guess x)
        guess
        (sqrt-iter (improve guess x) x)))
  (sqrt-iter 1.0 x))

I can't seem to wrap my head around around internal block structure. If the syntax for define is: (define procedure arg arg body). How are you able to define all the other local-scope variables inside the top-level define?

Is there a syntax exception for defines?

Thunder
  • 43
  • 3
  • It would be useful to know which Scheme you're using. Also, you may want to review [this question](https://stackoverflow.com/questions/20896471/decent-way-of-nested-definition-in-scheme) and its answers. – pdoherty926 Jul 12 '17 at 04:09

1 Answers1

1

The syntax for define is not (define procedure arg .. body). It is either (define (name . args) defines ... body1 bodyn ...) which is a shortcut for (define name (lambda args defines ... body1 bodyn ...)). Note that x ... means zero or more so (lambda (a b) (+ a b)) is fine but (lambda (a b) (define (test) (+ a b))) isn't.

Internal define is handled by the lambda syntax. Basically it gets rewritten to a letrec. So

(define (sqrt x)
  (define (good-enough? guess x)
    (< (abs (- (square guess) x)) 0.001))
  (define (improve guess x)
    (average guess (/ x guess)))
  (define (sqrt-iter guess x)
    (if (good-enough? guess x)
        guess
        (sqrt-iter (improve guess x) x)))
  (sqrt-iter 1.0 x))

Becomes:

(define sqrt
  (lambda (x)
    (letrec ((good-enough? (lambda (guess x)
                             (< (abs (- (square guess) x)) 0.001)))

             (improve (lambda (guess x)
                        (average guess (/ x guess))))
             (sqrt-iter (lambda (guess x)
                          (if (good-enough? guess x)
                              guess
                              (sqrt-iter (improve guess x) x)))))
      (sqrt-iter 1.0 x))))

Obviously the reason for using local define is to keep it flatter and more readable than letrec. Keeping the same name even though they are totally different beasts that are handled by different parts of the implementation is a simplification for scheme programmers but harder to grasp and understand if you try to figure out how scheme implementations work.

Sylwester
  • 47,942
  • 4
  • 47
  • 79
  • In R6RS and R7RS, internal definitions by 'define' are rewritten as letrec*, not letrec. A later definition can evaluate an earlier one. – Chris Vine Jul 13 '17 at 23:38