Most people are familiar with Python's
for syntax, and how it can be used with all sorts of iterable things.
One tool to make things iterable is the
yield statement. Here's a simple, hand-crafted iterable sequence (error-checking omitted for brevity):
def eachCharacterOf(theString):
while theString:
yield theString[0]
theString = theString[1:]
for x in eachCharacterOf("Hello world!"):
print x
OK, it's not the most efficient means of looping through each character, but you get the idea.
I mention this because
yield forms a critical component for a new approach to writing self-documenting programs in Python. You see, one of the most frequent complaints about Python (and languages like it) involves the inability to support RAII -- Resource Acquisition Is Initialization. This is a novel resource management technique that has found
extensive application in D and C++ programs of late. While not necessarily the most performant, it
works, and it doesn't involve a sophisticated run-time support in the form of a garbage collector. Moreover, it works with
any kind of resource, not just memory.
Lisp works around the lack of RAII by using, well, RAII -- Resource Acquisition Is
Invokation. This differs from RAII in that the body of some code, which utilizes some resource, is
called, and the scoping is dealt with in the body of the caller. For example:
;; Yes, I know this isn't valid Lisp code. Again, it's intended to
;; get the point across, not to be correct.
(defun with-some-resource (resource-name fn)
(let ((R (acquire-resource-by-name resource-name)))
(apply fn R) ; We invoke our body here.
(dispose-resource-safely R))) ; We dispose after our body has finished.
(defun foo ()
(with-some-resource "rsrc-name-here" #'(lambda (myR)
;; work with myR here.
)))
This pattern is used, quite literally,
all over the place in Lisp, and it really makes for quite readable programs.
(Footnote: most (with-whatever) forms are actually macros, so as to look cleaner and behave more like actual language features than library features.)
Python 3000 (aka Python 3.0) will include a
with: statement that enables the developer to create their own
with-constructs. However, it still involves the use classes, and you have to override the
__entry__ and
__exit__ methods, and it's all generally pretty ugly. The
biggest disadvantage, however, is that the code requires Python 3.0, whose roll-out is still going to take years, at least where I currently work.
The next best thing: single-loop iterators. Here's how I'd re-code the above
with-some-resource in Python 2.4,
right now.# Again, error-checking omitted for brevity
def a_resource_named(resourceName):
R = acquire_resource_named(resourceName)
yield R
R.disposeSafely()
for myR in a_resource_named("rsrc-name-here"):
# work with myR here.
Because precisely one
yield is invoked, the for "loop" only executes once. Moreover, when it is over, it properly discards any and all resources it previously acquired. As usual, employ
try/except/finally blocks as needed for error resistance.
In this manner, I can now employ Lisp-style RAII in Python using a syntax which is convenient, which fully encapsulates the core logic of the management of some resource, it works in any Python version that supports the
yield statement, and for which it works with
all kinds of resources, not just memory. And, the syntax is darn pretty too, as long as you choose a good procedure name.