Favorite non-python language trick?

Shai shai at platonix.com
Thu Jul 7 00:39:05 CEST 2005


I only saw this today... sorry about the late response. Anyway,
replying to your two messages at once:

Mike Meyer wrote:

> Last time I checked, dynamic binding variables were frowned on in LISP
> systems as well. Scheme doesn't have them.  Common LISP requires
> special forms to use them.

They're called "Special vars", and you need to define them (unlike
local LISP variables, which behave essentially like Python vars), but
then you use them just like other vars (that is, you usually bind them
with LET). This is the first I hear about them being ill-considered in
LISP; http://www.gigamonkeys.com/book/ is a recently published LISP
book which recommends them. I don't know about Scheme, but I think it
does have them.

The one "special" thing you see in every use of these vars in LISP is a
naming convention; as LISP symbols can contain most characters, they
are usually named with asterisks on both ends to distinguish them.
Thus, in the example above, the dynamic var would be named "*x*".

> The problem with the given use case is that it lets every routine in
> the call chain substitute it's own variable for the library parameter
> you want to use, with no local indication that this is going
> on. This makes bugs in dynamically scoped variables a PITA to find.

In LISP, the naming convention indeed takes care of that; and indeed, I
consider taking the LISP way would be better. The definition of x as
dynamic would then be not in bar nor its callers, but in the definition
of x, as in

dynamic x=10
def bar():
  print x

I specified the syntax as I did, specifically to make it match the
current definition of globals, which "enjoys" the same problems you
noted with my dynamics.

>
> >> x=10
> >> def foo():
> >>   # No need to define x as it is only read -- same as globals
> >>   print x
> >>
> >> def bar():
> >>   dynamic x
> >>   x = 11
> >>   foo()
> >>
> >> def baz():
> >>   bar() # prints 11
> >>   foo() # prints 10; the binding in bar is undone when bar exits
>
> Here's the problem with that. Consider this script:
>
> import foo
> x = 10
> def bar():
>     print x
>
> foo.foogle(bar)
>
> If foo.foogle includes "dynamic x" and then invokes bar, bar could
> print anything. This makes the behavior of bar unpredictable by
> examining the sourc, with no hint that that is going on.
>
While I didn't write it explicitly, if both LISP and Python globals are
to be followed, the dynamic x should somehow be defined in the scope of
its module. On second thought, this means "dynamic" _must_ be added in
the variable definition, for foo.foogle will simply access it as
"othermodule.x", which doesn't differentiate globals from dynamics.

Either way, Python as it is now allows foo.foogle to change x even
without dynamic variables; it is accessible as barmodule.x. bar()
should expect to have other functions mess with its globals, and
dynamics are no different.

> > Given that it's a feature I don't want programmers using, I'd only be
> > willing to see it added to the language if you can show that it has no
> > overhead so long as you don't use it. I'm not sure that can be done.
>
That sounds like a fine requirement. Now, with my corrected
proposition, it would be implementable at the module-object level, so
that only module which use the feature, and modules which use them,
would be affected.

> Here's a proposal for dynamically bound variables that you should be
> able to implement without affecting the runtime behavior of code that
> doesn't use it.
>
> Instead of dynamic meaning "all references to the named variable(s)
> will be dynamic until this function exits", have it mean "the named
> variable(s) will be dynamic in this function." Whether it should only
> check local variables in the calling routines, check local + global,
> or check for all free variables, is an open question.
>
> I.e. - your example would be written:
>
> x = 10
> def foo():
>     dynamic x
>     print x
>
> def bar():
>     x = 11
>     foo()
>
> def baz():
>     bar()       # prints 11
>     foo()       # Possibly an error?
>

This introduces the same problem you noted with my original proposal,
but in reverse: Now, in bar(), you define and use a local variable, and
suddenly some library function changes its behavior misteriously.

> For my example above, bar would *always* print 10. Nothing that
> foo.foogle did would change that. However, you could write:
>
> import foo
> def bar():
>     dynamic x
>     print x
>
> foo.foogle(bar)
>
> In this case, bar will print whatever foo.foogle sets x to - and it's
> noted in the source to bar. This means that functions that don't
> declare a dynamic variable can be compiled to the same code they are
> compiled to now.
>

This is, I believe, disproved by my comment above.

Thanks for your time and effort,

Shai.




More information about the Python-list mailing list