define letrec* semantics at REPL vs script/library

Asked by Derick Eddington on 2007-12-25

I'm pretty sure the exception because of f1 being an unbound object is correct behavior because of the letrec* semantics of define, but is the unbound variable exception because of f2 correct behavior? I'm porting R5RS code which does a let after a define using the same identifier like these and I expected it to have the behavior f1 does but then I tried it out at the REPL and got different behavior... I guess this is another subtle difference between R5RS and R6RS? But should Ikarus's REPL being doing what it did for f2?

Ikarus Scheme version 0.0.2patched+ (revision 1285, build 2007-12-24)
Copyright (c) 2006-2007 Abdulaziz Ghuloum

> (library (hmm)
    (export f1)
    (import (rnrs))
    (define f1
      (let ([%f1 f1])
        (lambda args (apply %f1 args)))))
> (import (hmm))
> (f1 1 2 3)
Unhandled exception
 Condition components:
   1. &assertion
   2. &who: apply
   3. &message: "not a procedure"
   4. &irritants: (#<unbound-object>)
>
> (define f2
    (let ([%f2 f2])
      (lambda args (apply %f2 args))))))
Unhandled exception
 Condition components:
   1. &assertion
   2. &who: eval
   3. &message: "unbound variable"
   4. &irritants: (f2)

Question information

Language:
English Edit question
Status:
Solved
For:
Ikarus Scheme Edit question
Assignee:
No assignee Edit question
Solved by:
Abdulaziz Ghuloum
Solved:
2007-12-26
Last query:
2007-12-26
Last reply:
2007-12-25
Best Abdulaziz Ghuloum (aghuloum) said : #1

Actually, the repl behavior in example 2 is correct: because f2 is
unbound when you tried to reference it, you got an error exactly at
that point

(define f
   (let ([f f]) ; <- this reference is invalid because f does not
have a value yet
     <something>))

In example 1, you should've received the exact same error, but you
didn't. Instead, you got an error later on when you attempted to use
the value (which shouldn't have been there in the first place).

(library (hmmm)
   (export f1)
   (import (rnrs))
   (define f
     (let ([f f]) ; <-- you should've gotten an error here
       (lambda args
         (apply ; <-- but you got it here
           f args)))))

The reason, of course, is because it's a bug :-). Ikarus should
detect references/assignments to unbound letrec/letrec* variables,
but currently it doesn't---it just evaluates the variable, which
returns this #<unbound-object>.

Now we cannot insert a check before every reference (that would be
waaaay too expensive), and there is an algorithm for reducing the
number of inserted checks (listed in Dybvig et al's "Fixing Letrec"
paper http://www.cs.indiana.edu/~dyb/pubs/fixing-letrec.pdf). One
day I'll get around to implementing it :-).

The "Fixing Letrec" paper is indeed illuminating. I must confess I've
only read the R5RS once and still haven't had the time to read all of
the R6RS, but if I had, I would've know about the letrec restriction.
Time I read it all, I think :)

I'm curious, why does the REPL detect the reference to the unbound
variable but a library/script does not?

Thanks Abdulaziz Ghuloum, that solved my question.