Unbound variable in Racket-Scheme

I am trying to implement a data structure to store the state (bind variables) of a program. I represent a state with association lists, which are lists of key-value pairs.

Example of associations list: '((x . 3) (y . (1 2)) (z . a))

. In this example, the key 'x

matters 3

, 'y

matters, '(1 2)

and 'z

matters 'a

.

To manage the state, I have two functions:

  • (get-binding state var)

    This function returns the var value character associated (assigned) to the state.

  • (set-binding state var val)

    This function returns a new state that is the same as the state, except that var is bound to val.

  • empty-state

    This variable corresponds to the unbound state. It is defined using define. Note: empty state is not a function.

Example:

> (get-binding (set-binding (set-binding empty-state 'x 3) 'x 4) 'x)

      

which outputs 4

.

Here is my code:

(define (enclosing-environment env) (mcdr env))

(define empty-state null)

(define (get-binding env var)
  (define (env-loop env)
    (define (scan vars vals)
      (cond [(null? vars)
             (env-loop (enclosing-environment env))]
            [(eq? var (mcar vars))
             (mcar vals)]
            [else
             (scan (mcdr vars)
                   (mcdr vals))]))
    (if (eq? env empty-state)
        (error "Unbound variable:" var)
        (let ([frame (first-frame env)])
          (scan (frame-variables frame)
                (frame-values frame)))))
  (env-loop env))

(define (set-binding! env var val)
  (define (env-loop env)
    (define (scan vars vals)
      (cond [(null? vars)
             (env-loop (enclosing-environment env))]
            [(eq? var (mcar vars))
             (set-mcar! vals val)]
            [else
             (scan (mcdr vars)
                   (mcdr vals))]))
    (if (eq? env empty-state)
        (error "Unbound variable -- SET!:" var)
        (let ([frame (first-frame env)])
          (scan (frame-variables frame)
                (frame-values frame)))))
  (env-loop env))

      

But the error message "Unbound variable - SET !: x" appears. How can I prevent this?

+3


source to share


3 answers


Do you understand that get-binding!

both set-binding

are the same function, except for two lines? I suggest you combine them into one function:

(define (get/set-binding when-found when-unbound env var (val))
  (define (env-loop env)
    (define (scan vars vals)
      (cond [(null? vars)
             (env-loop (enclosing-environment env))]
            [(eq? var (mcar vars))
             (when-found val vals)]
            [else
             (scan (mcdr vars)
                   (mcdr vals))]))
    (if (eq? env empty-state)
        (when-unbound var)
        (let ([frame (first-frame env)])
          (scan (frame-variables frame)
                (frame-values frame)))))
  (env-loop env))

      

Then you can fix the error. The problem is that there is no way to change null

, so you cannot enter the binding by changing the argument. You can only do this through the return value. You can define set-binding!

like this:

 (define (set-binding! env var val)
     (set/get-binding (λ (val vals)
                         (set-mcar! vals val)
                         env)
                      (λ (var)
                         (add-binding var val env)) env var val))

      

To determine add-binding

, I have to make some guesses about the structure env

based on your code. I am assuming that env

is a mlist of frames and that a frame

:

(define-struct frame (variables values))

      

I also assume that if env

has any frames, you want to add a new variable to the first frame.



If these assumptions are correct, then it add-binding

will be determined as follows:

(require compatibility/mlist)

(define (add-binding var val env)
  (if (null? env)
      (mlist (make-frame (mlist var) (mlist val)))
      (let ((first (mcar env)))
        (set-frame-variables! (mcons var (frame-variables first)))
        (set-frame-values! (mcons val (frame-values first)))
        env)))

      

Since this version set-binding!

returns a new one env

, if you pass null

for env

, you always need to assign the return value set-binding!

:

Correct use:

(set! env (set-binding! env var val))

      

Improper use:

;; If env is null, the new binding is immediately garbage-collected.
(set-binding! env var val)

      

+1


source


That's a lot of code ...

What if "environment" is a function looking for a value associated with a name? To get the value we need is to apply the environment to the name:

(define (env-get env name)
  (env name))

      

And then, to extend the environment by creating another, we look up the name, but if we don't find it, we defer the base environment:

(define (env-set env name value)
  (lambda (nname)
     (if (eq? nname name)
         value
         (env nname))))

      



But, if the environment is empty, it's just an error.

(define env-empty
  (lambda (nname)
    (error "Unbound Variable: " nname)))

      

Example:

> (define env-1 (env-set env-empty 'x 4))
> (define env-2 (env-set env-1 'y '(a b)))
> (env-get env-2 'x)
4
> (env-get env-2 'y)
(a b)
> 

      

+1


source


Premises

  • The error is indicated in the code.

  • It is called when the value env

    passed to set-binding!

    is eq?

    to null

    , aliased as empty-state

    .

  • env

    is compared to null

    before (var . val)

    added to env

    .

Hypothesis

The source environment is empty ... i.e. null

or an alias like empty-environment

.

Decision

Do not treat (eq? env empty-state)

as an error condition ... at least not until initialization.

0


source







All Articles