The third version of FIOC ( Python3000 for you cheese lovers ) is kind of lame, even lamer than classic FIOC. Minor changes suck and bigger ones weren't necessary.
As someone who doesn't know Python, I have 3 questions:
1) Does Python's lambda's and functions (as well as other things like classes) allow you to lexically bind something over them so as to create a closure?
2) If 1 is true, can you interconnect lambdas/closures to form networks of closures, usually combined with TCO to effectively make all the connections into goto's. This is useful for many things, one of them being state machines.
3) If 2 is true, can you generate said lambdas and networks of closures at compile time or runtime, for example by using a form of macros, or some sort of pattern-based code generation
4) If 3 is true, can it be done in a mostly hygenic macro(if not in a full-fledged system, at least using real Python code and some sort of gensyms? or is it all limited to primitive string substituation instead, so no operating on real objects?)
1) Does Python's lambda's and functions (as well as other things like classes) allow you to lexically bind something over them so as to create a closure?
Yes and no.
Lambdas in Python are more trouble than they're worth. You can write local functions, and they're closures, so you can do everything a lambda can do. You just can't do it in one line.
Beyond that, you're in "l33t feature" territory, where the code probably won't work right and nobody can figure it out.
Python's inner functions are retarded. >>> def foo():
... def bar():
... print x
... x = 9
... return bar
...
>>> foo()()
9
There is no good reason that x should be in scope there.
>>52
enlighten me, I can think of a couple of bad reasons, but no good ones
Name:
Anonymous2010-04-20 5:34
>>51
I AM 12 AND WHAT IS THIS?
BAR() IS NOT PURE FUNCTION
EVEN IF WE CHANGE PRINT TO PASS
RIGHT?
Name:
Anonymous2010-04-20 9:42
>>51
That has nothing to do with inner functions whatsoever. >>> x = 10
>>> def f():
... print x
... x = 20
...
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in f
UnboundLocalError: local variable 'x' referenced before assignment
Name:
Anonymous2010-04-20 11:03
>>55
Actually, you are right. It can be done quite easily from the top level. However, inside that inner function I would have expected it to reference the 'x' in the global scope and not in the outside functions scope, which had been modified. My issue is that pythons scope is either not static, or it does an implicit letrec*.
And you still haven't told me why this is a good thing ;)
>>55
I'm not sure where you were going with that example, the local variable that is referenced before assignment is in fact the second one. >>> x = 10
>>> def f():
... print x
...
>>> f()
10
>>> def f():
... print x
... x = 20
...
>>> f()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in f
UnboundLocalError: local variable 'x' referenced before assignment
>>>
Name:
Anonymous2010-04-20 13:38
>>57
I recommend using use strict in every program
Name:
Anonymous2010-04-20 14:06
>>56,57
My point is: that's how Python scopes work. The bytecode compiler statically identifies every name that belongs to a scope. Any expression that uses the scope has access to every variable of the scope, no matter where it is first assigned or used, lexically.
It is not a good thing in the whole scheme of things, I personally prefer explicit declaration with additional safeguards a la C#.
However, it's the best one can have given the "assignment is declaration" rule. Accounting for relative positions of assignment in the scope would produce subtle errors with horrible consequences.
Also, how is the following code supposed to work? Is correct scope determined statically or dynamically?
x = 10
def f(n):
if n: x = 20
print x
Name:
Anonymous2010-04-20 14:15
>>59 >>> x = 10
>>> def f(n):
... if n: x = 20
... print x
...
>>> f(5)
20
>>> f(0)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 3, in f
UnboundLocalError: local variable 'x' referenced before assignment
I humbly expected f(0) printing 10.
Name:
Anonymous2010-04-20 15:02
This makes no sense. Why would it bother looking around the function for assignments?
>>62
It's not dynamic scoping. Dynamic scoping is when you call f(0) from a function that defines `x` and its value is used when no local variable with that name is found. In no circumstances it is looked up in lexically outer environments, those are not supposed to be accessible at all.
The approach that >>60 expected is literally a nameless horror lurking under the thin veneer of civilization. It's so evil that no one has really been far even as decided to use even go and name it.
Name:
Anonymous2010-04-20 17:29
It's so evil that no one has really been far even as decided to use even go and name it.
I propose `mutable scoping' or 'guidoing'
Question for >>- (I couldn't be bothered picking out the relevant posts)
How do you expect to do code such as the following:1 def median(a, b):
return int((a + b) / 2)
def foo(a, b):
m = median(a, b)
if m != a != b:
return [a] + bar(m, b)
return list(range(a, m))
def bar(a, b):
m = median(a, b)
if m != a != b:
return foo(a, m) + [b]
return list(range(m, b))
print(bar(0, 20))
Without some way of being able to reference variables which have not yet been assigned in the scope? If you had to forward declare every function that hasn't been defined yet, you might as well be writing header files and using C.
This isn't something unique to Python, either. Perl does exactly the same thing: sub a {
return b($in_a, @_);
}
sub b {
print($in_b, "@_\n");
}
$in_a = "in a:";
$in_b = "in b:";
$, = " ";
a(1, 2, 3);
While it can be counterintuitive at times, the reasons behind this behavior are completely sound, and you likely rely on it much more often than you might think.
1. Yes, these examples are rather contrived, but they're meant not to demonstrate practicality so much as to get the point across succinctly. If this bothers you, pretend I wrote a full recursive descent parser.
Name:
Anonymous2010-04-21 6:12
>>66
I agree that you need mutually recursive functions, but I disagree that you need to be able to mix definitions and expressions. If you prohibit definitions (whether it be class or function) after a nondefinition then you can still make meaningful assumptions about scope. Otherwise you need to be aware of everything in the file declared at that level or higher to know if something is in scope. >>> def foo():
... def bar():
... return x
... # there could be an arbitrary amount of code in here
... print "nearly done"
... x = 9
... return bar
...
>>> foo()()
nearly done
9
>>>
While it can be counterintuitive at times, the reasons behind this behavior are completely sound, and you likely rely on it much more often than you might think.
If you mean, "do I rely on mutually recursive definitions?" then yes, but I do not mix expressions and definitions.
Name:
Anonymous2010-04-21 6:15
>>67
If you want an example of how I think it should be done, look at functional languages. They make scoping explicit with constructs like Schemes letrec* and Haskells let.
Name:
Anonymous2010-04-21 6:19
>>66
you're not referencing any variables before they've been assigned in that code.
Name:
Anonymous2010-04-21 8:25
>>66
The problem is solved by converting undefined variable lookups to lookups in current module.
Name:
Anonymous2010-04-21 16:04
>>67 you need to be aware of everything in the file declared at that level or higher
That already happens, because the AST is collecting references to variables, and to assignments, before any actual execution takes place.
>>69
Yes, I am. def is an assignment. If that makes you uncomfortable, try rewriting the code using lambda instead. Same net effect.
>>70
Doing that causes problems with functions like those given which occur in a nested context.
Name:
Anonymous2010-04-21 16:24
Personally I think letrec is a dumb hack.
Reasonable men may differ, but I fail to see how, in the mutual recursion case, having letrec is different than having what appears to be a mutable scope. A specific construct like letrec allows you to have other different ways to precisely control scope(let,let*,letrec*,module system foo), rather than having this one size fits all mechanism.
>>74 def causes the name of the function to be added to the local name dictionary if it doesn't already exist, with the function body as its value. If that's not assignment, please tell me what the fuck it is.
Traceback (most recent call last):
File "<pyshell#24>", line 1, in <module>
five()
File "<pyshell#23>", line 2, in five
print five UnboundLocalError: local variable 'five' referenced before assignment