schemeracket

Don't understand Scheme "unbound identifier" message for this function definition


For this code (using Racket IDE) when doing "Run"

#lang scheme

(define (queue! q x)
  (let ( (b (list x))
       )
    (if (null? (car q))
        (set-cdr! q b)
        (set-cdr! (car q) b))
    (set-car! q b)))

I get this error that I don't understand as I see q as a parameter and b as being defined:

set-cdr!: unbound identifier in: set-cdr!

The Racket IDE is highlighting the first set-cdr! as the one in error: (set-cdr! q b)

Edit: It turns out that "#lang scheme" should not be in Racket. Using it does not give you R5RS, R6RS or R7RS. "#lang r5rs" is what allows old code samples to run in Racket, such as from the Scheme Cookbook. Apparently using "#lang scheme" was how you showed you were going to use the PLT Scheme language (renamed to Racket in June 2010). But since PLT Scheme had some differences from R5RS Scheme it should have been "#lang PLTscheme".


Solution

  • You have uncovered the reason that I don't recommend using Racket to learn Scheme. Racket (and by extension Racket's #lang scheme) uses immutable pairs, which means that lists and all other cons-pair-based structures are immutable. Scheme as standardized in the RnRS documents does not use immutable pairs. set-cdr! and set-car! are pair mutation procedures in Scheme, but they don't exist in vanilla Racket. Since scheme/base is derived from racket/base these mutation procedures don't exist in #lang scheme either, and this is why the unbound identifier error is issued.

    In Racket you need to be explicit about using mutable pairs. mcons and friends are available for working with mutable pairs in racket/base and by extension in scheme/base and scheme. The posted code could be modified thus:

    #lang scheme
    
    (define (queue! q x)
      (let ((b (mcons x '())))
        (if (null? (mcar q))
            (set-mcdr! q b)
            (set-mcdr! (mcar q) b))
        (set-mcar! q b)))
    

    You could also use the r5rs language instead of scheme. This language does define set-car! and set-cdr! and allows their use on regular pairs (which are mcons pairs under the hood).

    If r5rs isn't acceptable, r6rs can be used, but this requires a bit more ceremony in that a few libraries need to be manually imported:

    #lang r6rs
    
    (import (rnrs lists (6))
            (rnrs base (6))
            (rnrs io simple (6))
            (rnrs mutable-pairs (6)))
    
    (define (queue! q x)
      (let ((b (list x)))
        (if (null? (car q))
            (set-cdr! q b)
            (set-cdr! (car q) b))
        (set-car! q b)))