TITLE: Follow Up to "Bug or Feature" AUTHOR: Eugene Wallingford DATE: April 28, 2009 9:27 AM DESC: ----- BODY: Thanks to several of you who have pointed out that Scheme behaves exactly Python on the Python example in my previous post:
    > (define (f x) (if (> x 0) (f (- x 1)) 0))
    > (define g f)
    > (define (f x) x)
    > (g 5)
    > 4
Sigh. Indeed, it does. That is what I get for writing code for a blog and not test-driving it first. As an instructor, I learned long ago about the dangers of making claims about code that I have not executed and explored a bit -- even seemingly simple code. Now I can re-learn that lesson here. The reason this code behaves as it does in both Scheme and Python is that the bindings of function f don't involve a closure at all. They refer to a free variable that must be looked up at in the top-level environment when they are executed. While writing that entry, I was thinking of a Scheme example more like this:
    (define f
      (lambda (x)
        (letrec ((f (lambda (x)
                      (if (> x 0)
                          (f (- x 1))
                          0))))
          (f x))))
... in which the recursive call (f (- x 1)) takes place in the context of a local binding. It is also a much more complex piece of code. I do not know whether there is an idiomatic Python program similar to this or to my earlier Scheme example:
    (define f
      (let ((i 100))
        (lambda (x)
           (+ x i))))
    (define i 1)
    (f 1)
If there is, I suspect that real Python programmers would say that they simply don't program in this way very often. As van Rossum's The History of Python blog points out, Python was never intended as a functional language, even when it included features that made functional-style programming possible. So one ought not be too surprised when a purely functional idiom doesn't work out as expected. Whether that is better or worse for students who are learning to program in Python, I'll leave up to the people responsible for Python. One can find all kinds of discussion on the web about whether Python closures are indeed "broken" or not, such as here (the comments are as interesting as the article). Until I have a little more time to dig deeper into the design and implementation of Python, though, I will have to accept that this is just one of those areas where I am not keeping up as well as I might like. But I will get to the bottom of this. Back to the original thought beyond my previous post: It seems that Python is not the language to use as an example dynamic scope. Until I find something else, I'll stick with Common Lisp and maybe sneak in a little Perl for variety. -----