4 ms·
It is, but in this case the variable is in global scop rather than lexical. You get the same behaviour if you translate the example into e.g. Scheme. The issu
by pherq 12y ago
It is, but in this case the variable is in global scop rather than lexical. You get the same behaviour if you translate the example into e.g. Scheme.
The issue is that the name referred to in the recursive call is a global variable access, and so redefining the value of that variable produces some strange effects. However the simple solution to this (which basically boils down to having no global variables, just lexical variables at the top level) has other problems -- it would require that every function be defined before it is used, which would enforce a bottom-up ordering to the file. If you want to be able to place the definitions in the file in any order, and you want to have the ability to redefine functions, then this is pretty much inevitable (even if you special case simple recursion like this, you probably can't reasonably deal with mutual recursion).
If you need to refer to a particular function without the risk of it being redefined, it is possible (at least in Scheme) by abusing let and set! a little...
(define countdown #f)
(letrec ((cd
(lambda (n)
(if (> n 0)
(cons n (cd (- n 1)))
'(liftoff)))))
(set! countdown cd))
I don't know Clojure enough to say what the equivalent would be, but it should make sense anyway (the (define x #f0) ... (set! x y) is to produce a top-level definition of x).
In this case, the recursive call is to a lexically scoped variable inside the let, that is then preserved in the closure, thus the closure will always call itself regardless of what is done to the names at the top level. You can also use the Y combinator to do it (again, the recursive call is made to a variable in lexical scope rather than global scope).
- waterhouse 12y agoMy brain automatically refactors what you wrote into the following: (define countdown (letrec ((cd (lambda (n) (if (> n 0) (cons n (cd (- n 1))) '(liftoff))))) cd)) Which should be equivalent. As for what Clojure provides, the Clojure docs say this: "No letrec, labels or flet - use (fn name [args]...) for self-reference, letfn for mutual reference. Thus, this would be the Clojure version: (def countdown (fn cd [n] (if (pos? n) (cons n (cd (- n 1))) '(LiftOff)))) In fact, it also works to use the name "countdown" in the fn form. You could define your own version of "defn" that used fn like this, to make internally self-recursive functions (as opposed to ones that go through the current global binding of the function's name) by default. I am not actually sure that this is a bad default behavior. (The main problem that comes to mind is if someone replaces it with a "traced" version, that debug-prints its arguments in addition to what it normally does, and expects to see debug-printing of the recursive calls.)
- p4bl0 12y agoI see, thanks for the clarifications. > having no global variables, just lexical variables at the top level That is what OCaml does. Compare Racket: Welcome to Racket v5.3.6. > (define (countdown n) (if (> n 0) (cons n (countdown (- n 1))) '(LiftOff))) > (countdown 10) '(10 9 8 7 6 5 4 3 2 1 LiftOff) > (define cd countdown) > (countdown 10) '(10 9 8 7 6 5 4 3 2 1 LiftOff) > (cd 10) '(10 9 8 7 6 5 4 3 2 1 LiftOff) > (define (countdown n) (if (> n 0) (cons n (countdown (- n 2))) '(LiftOff))) > (countdown 10) '(10 8 6 4 2 LiftOff) > (cd 10) '(10 9 7 5 3 1 LiftOff) to OCaml: OCaml version 4.01.0 # let rec countdown n = if n > 0 then n :: (countdown (n - 1)) else [0];; val countdown : int -> int list = <fun> # countdown 10;; - : int list = [10; 9; 8; 7; 6; 5; 4; 3; 2; 1; 0] # let cd = countdown;; val cd : int -> int list = <fun> # cd 10;; - : int list = [10; 9; 8; 7; 6; 5; 4; 3; 2; 1; 0] # let rec countdown n = if n > 0 then n :: (countdown (n - 2)) else [0];; val countdown : int -> int list = <fun> # countdown 10;; - : int list = [10; 8; 6; 4; 2; 0] # cd 10;; - : int list = [10; 9; 8; 7; 6; 5; 4; 3; 2; 1; 0] which is way better in the "principle of least astonishment" department (at least in that case).