djhaskin987
djhaskin987

Reputation: 10107

In Scheme, when a recursive function returns a list, I cannot assign it to a variable

I have the following code:

(define (get-data)
  (define port (open-input-file "../data/problem11.txt"))
  (define field 0)
  (define (get-fields)
    (define field (read port))
    (cond ((not (eof-object? field)) 
           (cons field (get-fields)))  
          (else '())))

  (define returned (get-fields))
  (close-input-port port)
  returned)

(get-data)

I can't find any problem with my code according to the manuals, and nothing comes up on google when searched, but when I run the code, SCM (my chosen scheme interpreter) gives me the following error:

;ERROR: "/usr/lib/scm/Iedline.scm": unbound variable:  get-fields
; in expression: (get-fields)
; in scope:
;   (returned get-fields field port . #@define)
;   ()  procedure get-data
;STACK TRACE
1; (#@define ((returned (get-fields)) (get-fields (#@lambda () (# ...
2; (#@get-data)

But when I make my code look like this, it works fine:

(define (get-data port)
  (define field 0)
  (define (get-fields)
    (define field (read port))
    (cond ((not (eof-object? field))
           (cons field (get-fields)))
          (else '())))
  (get-fields))


(define port (open-input-file "../data/problem11.txt"))
(define alfa (get-data port))
(close-input-port port)

Why is it that when I attempt to define returned as the returned list of get-fields that I get an unbound variable error, but when I define alfa as the returned list of get-data (in the second code block), it works okay? Is it that get-fields is recursive? I don't get it. Any answers here would be awesome, thanks.

Upvotes: 3

Views: 202

Answers (1)

C. K. Young
C. K. Young

Reputation: 223183

Implementations are allowed to implement internal defines using one of two possible semantics: letrec and letrec*.

SCM has obviously chosen to use letrec semantics. That means that, given a bunch of internal defines, none of them can immediately refer to the value of another internal define in that same bunch: the same restriction that applies to letrec.

Some implementations, like Racket, use letrec* semantics. That means that any variable defined in an earlier internal define can be used directly by later internal defines. (In other words, your code will work fine in any letrec*-based implementation.)

Since you're using a letrec-based implementation, do this:

(let ((returned (get-fields)))
  (close-input-port port)
  returned)

Upvotes: 6

Related Questions