global define
First off, top level programs are handled by a different part of the implementation than in a function and defining an already defined variable is not allowed.
(define a 10)
(define a 20) ; ERROR: duplicate definition for identifier
It might happen that it works in a REPL since it's common to redefine stuff, but when running programs this is absolutely forbidden. In R5RS and before what happened is underspecified and didn't care since be specification by violating it it no longer is a Scheme program and implementers are free to do whatever they want. The result is of course that a lot of underspecified stuff gets implementation specific behaviour which are not portable or stable.
Solution:
set!
mutates bindings:
(define a 10)
(set! a 20)
define
in a lambda (function, let, ...)
A define
in a lambda is something completely different, handled by completely different parts of the implementation. It is handled by the macro/special form lambda
so that it is rewritten to a letrec*
A letrec*
or letrec
is used for making functions recursive so the names need to be available at the time the expressions are evaluated. Because of that when you define
n
it has already shadowed the n
you passed as argument. In addition from R6RS implementers are required to signal an error when a binding is evaluated that is not yet initialized and that is probably what happens. Before R6RS implementers were free to do whatever they wanted:
(define (func n)
(define n (+ n 1)) ; illegal since n hasn't got a value yet!
n)
This actually becomes:
(define func
(lambda (n)
(let ((n 'undefined-blow-up-if-evaluated))
(let ((tmpn (+ n 1)))
(set! n tmpn))
n)))
Now a compiler might see that it violates the spec at compile time, but many implementations doesn't know before it runs.
(func 5) ; ==> 42
Perfectly fine result in R5RS if the implementers have good taste in books.
The difference in the version you said works is that this does not violate the rule of evaluating n
before the body:
(define (func n)
(define n 5)
n)
becomes:
(define func
(lambda (n)
(let ((n 'undefined-blow-up-if-evaluated))
(let ((tmpn 5)) ; n is never evaluated here!
(set! n tmpn))
n)))
Solutions
Use a non conflicting name
(define (func n)
(define new-n (+ n 1))
new-n)
Use let
. It does not have its own binding when the expression gets evaluated:
(define (func n)
(let ((n (+ n 1)))
n))