Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scheme Scoping (define and let)

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"

like image 825
user2085086 Avatar asked Feb 18 '13 23:02

user2085086


People also ask

What is let form in Scheme?

In Scheme, you can use local variables pretty much the way you do in most languages. When you enter a let expression, the let variables will be bound and initialized with values. When you exit the let expression, those bindings will disappear.

What is let * In racket?

Local Binding: let, let*, letrec, ... in The Racket Reference also documents let. A let form binds a set of identifiers, each to the result of some expression, for use in the let body: (let ([id expr] ...) body ...+)

What are the types of scoping?

For the purpose of their discussion, they define four (slightly different) kinds of scoping rules: trivial: no free variables allowed. static: a free variable takes its value from a set of global variables. lexical: a free variable takes the value of the binding that was in effect when the function was defined.

How let and Letrec constructs work in Scheme?

In a let expression, the initial values are computed before any of the variables become bound; in a let* expression, the bindings and evaluations are performed sequentially; while in a letrec expression, all the bindings are in effect while their initial values are being computed, thus allowing mutually recursive ...


2 Answers

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
  )
like image 194
Greg Hendershott Avatar answered Nov 01 '22 14:11

Greg Hendershott


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)>
> 
like image 1
GoZoner Avatar answered Nov 01 '22 14:11

GoZoner