Scheme Scoping (define and let)
Asked Answered
M

3

5

So I know that in Scheme define is for dynamic scoping and let for static scoping, yet the following thing confuses me:

If I have

(let ((x 0))
  (define f (lambda () x))
  (display (f))
  (let ((x 1))
    (display (f))
    )
  )

It will display 00. So far so good. However, if I add an extra define for x like so:

(let ((x 0))
  (define f (lambda () x))
  (display (f))
  (define x 4)
  (let ((x 1))
    (display (f))
    )
  )

It will display undefined4. Why is this? Why does defining x after evaluating f affect the returned value of (f)? And why is the return value "undefined"?

It is also worth mentioning that binding f with letrec instead of define will also work:

(let ((x 0))
  (letrec ((f (lambda () x)))
  (display (f))
  (define x 4)
  (let ((x 1))
    (display (f))
    )
  )
)

Returns 00.

Note: I have used DrRacket with the languge set on "Pretty Big"

Monolatry answered 18/2, 2013 at 23:6 Comment(5)
What do you mean by "define is for dynamic scoping in Scheme" ? BTW, unless you are required to use it for a course, "Pretty Big" is an obsolete dialect.Mulatto
Scheme always uses static scoping, it's incorrect to state that "define is for dynamic and let for static scoping"Confer
I agree with Oscar: the premise in the question is either wrong, or is using the wrong term.Demilune
Define allows dynamic scoping. Look at the following example: (define x 1) (define f (lambda() x)) (display (f)) (define x 2) (display (f)) Displays 12 As oposed to: (let ((x 1)) (letrec ((f (lambda() x))) (display (f)) (let ((x 2)) (display (f)) ) ) ) which displays 11Monolatry
@user2085086: no, you are confusing the effect of two toplevel defines, the second of which is re-assigning the first. In some implementations of Scheme, redefinition will be treated as a set!. But this is troublesome and confusing. In fact, your first code snippet won't even compile in standard Racket, as the compiler will say up front that the duplicate definition is illegal.Demilune
I
6

The issue you're experiencing in the second case is that (define x 42) makes x a variable for the entire scope in which it's defined. Now, although the variable is defined for the entire scope, its value is undefined until the actual (define x 42) line.

(let ()
  ;; up here, `x` is defined but has an undefined value
  ;; ...
  (define x 42)
  ;; down here `x` has the value 42
  ;; ...
  )

It's acting more like this:

(let ([x 'undefined])
  ;; ... up here, `x` is 'undefined
  (set! x 42)
  ;; ... down here, `x` is 42
  )
Ichthyornis answered 19/2, 2013 at 2:0 Comment(1)
Bun where goes let ((x 0)) at the top?Buckie
I
1

Your second and third code snippets are not Scheme (none of R5RS, R6RS nor R7RS). The <body> (of a let and others) is defined as:

<body> -> <definition>* <sequence>
<sequence> -> <command>* <expression>
<command> -> <expression>

and thus a define (which is a <definition>) cannot follow display (an <expression>). You are likely getting confusing results because the compiler/interpreter is incorrectly handling the expansion of 'let'.

Here is what a 'good' R6RS compiler does:

> (let ((x 0))
  (letrec ((f (lambda () x)))
  (display (f))
  (define x 4)
  (let ((x 1))
    (display (f))
    )
  )
)
Unhandled exception
 Condition components:
   1. &who: define
   2. &message: "a definition was found where an expression was expected"
   3. &syntax:
       form: (define x 4)
       subform: #f
   4. &trace: #<syntax (define x 4)>
> 
Ironmonger answered 25/2, 2013 at 4:18 Comment(0)
S
0

Case 1: the body of f binds to the outermost let in both invocations, resulting in 00 as static scope requires.

Case 2: I'm not very sure about this, but the internal (define x 4) shadows the outermost x=0 binding, and is in scope throughout even though it's textually after the call to f. Then some order of evaluation trickiness makes the first call happen before the new x binding is fully initialized, so it's "uninitialized". The second call in the inner let happens after everything is initialized, so 4.

Case 3: Now that we have explicitly put the letrec and the define in separate scopes, f obviously refers to the outermost let. The define does nothing here.

Shrewmouse answered 19/2, 2013 at 1:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.