Dan Sugalski:
Anyway, a continuation is essentially a closure that, in addition to closing over the lexical environment, also closes over the control chain. (Well, OK, control stack, but if you’re doing continuations odds are it’s a singly linked list rather than a real stack.) CS texts generally go on about continuations being “the rest of the program” or “gotos with arguments” or suchlike stuff. If those float your boat, great—they never made sense to me.
Yeah, I think it depends on whether you are looking at this from the angle of implementation or theory. If you’re writing a VM, then you think in terms of closing over the control chain. If you’re doing denotational semantics, you think in terms of lambdas for the rest of the program.
Update: Here’s part 2.
Alexei Kosut:
So I rewrote this thing yet again. I didn’t use any of Omni or Camino’s code, although having seen their implementations did make mine go pretty quickly. And now I’m relatively happy, since my table-like view works, and the licensing works out the way I wanted. But still, it seems like there’s something wrong that there’s all that source code out there that does exactly what I want, and that I can even download and look at, but cannot use. Were I a more philosophical or political person, I might even have something Important to say about it.
IMO, this kind of view should really be part of Cocoa to begin with, as it is in Java.
Joe Osborn’s OSFoundation framework adds blocks to Objective-C. The context for the block is passed in manually, and the code is specified as a string that’s interpreted at runtime.
Chris Kane mentions that you can do something similar using GCC 3.1’s nested functions and statement expressions. Blocks created in this way have lexical scope, but not dynamic extent, and the syntax is not great.
Erik Barzeski posted about iterators in Cocoa, so I thought I’d give some perspectives from other languages. I think there are really two concepts here: iteration and generation.
I don’t know that everyone agrees with these definitions, but to my mind iteration is about obtaining a sequence of values, without caring about how those values are produced. This is embodied by the NSEnumerator interface, java.util.Iterator, and the Python iteration protocols. Iteration is about how you get the values that you want to consume.
Here’s what iteration looks like in Cocoa:
NSEnumerator *enumerator = [objectStructure objectEnumerator];
id o;
while ( o = [enumerator nextObject] )
// do something with o
I use a C macro so that I can write it like this:
foreach ( o, [objectStructure objectEnumerator] )
// do something with o
Some languages, like Python, have a syntax for iteration:
for o in objectStructure:
# do something with o
In the above example, Python will call objectStructure’s __iter__() method to get an iterator object.
Generation is about how to produce the values. The generator returned by -[NSArray objectEnumerator] probably keeps a reference to the array and remembers an index into that array. When you send it -nextObject, it increments its counter and returns another object. java.util.Iterator is similar. This is straightforward to implement for list-like things. For instance, my SQLite wrapper includes an NSEnumerator subclass for rows in a database table. It took seconds to write. But how do you write this kind of generator for the nodes in a tree? Or the interesting tokens in a mail message? You need to remember more state than just an index, and it can be a real pain. In practice, people often perform a full traversal of the structure, stuffing the objects into an array, and then return the array’s generator. That makes the code much simpler, but inefficient.
I’ve seen two other ways of writing generators, which work better in the more complex cases. What I call the closure approach inverts the problem. Instead of writing code that gets values from a generator object and then does something with them, you put your code in a closure (a.k.a. lambda or block) and give the closure to the generator. This is the approach taken by Smalltalk and Ruby, and it could also work in other languages with closures, such as Lisp and Perl. From the caller’s perspective, we get something like this (in Ruby):
objectStructure.each { |o|
# do something with o
}
Here, the block (in curly braces) becomes an argument to the method call. The each method traverses objectStructure and evaluates the block many times, passing in different o’s.
Other languages use what I call the yield approach, which was popularized by CLU in the mid-70s. Under this approach, generation is handled by a function that can, in effect, return multiple times. Instead of using a return statement to return, it uses a yield statement. yield is like return, except that the next time the function is called, it resumes after the last-executed yield statement.
Here’s an example in Python:
# A recursive generator that generates Tree leaves in in-order.
def inorder(t):
if t:
for x in inorder(t.left):
yield x
yield t.label
for x in inorder(t.right):
yield x
Is that beautiful or what?
Interestingly, the keyword that Ruby uses to evaluate a block parameter is also called yield. It’s used for roughly the same reasons, but what’s going on under the hood is quite different. Ruby’s yield is like a function call, whereas CLU/Python’s is like a function return.
I like return-style yield better. It can even be used to create infinite generators (a.k.a. streams):
def odds():
i = 1
while True:
yield i
i = i + 2
Unfortunately, there is no yield, of either kind, in the C family. You can kind of fake it using macros.
Nicholas Riley links to iAppViews, code by Evan Jones that apparently demonstrates best practices for alternating background colors in NSTableView and NSOutlineView.
MagicHat reconstructs @interfaces from Objective-C frameworks and attempts to find the corresponding header files and documentation. Unlike class-dump, it provides a real human interface, although it’s more difficult to use MagicHat on arbitrary files.
Good thing my Mac can boot into OS 9. DiskWarrior 3, which is OS X–native, isn’t out yet, and last week I had to use DiskWarrior 2 to fix my internal drive before the Mac would even boot. So much for a stable operating system protecting against catalog damage. DiskWarrior is great, but with all the files Mac OS X installs it takes hours and hours to run. I wonder what Alsoft plans for PlusOptimizer.
Here’s an interesting alternative to Subversion.
Meta-CVS is a version control system built around CVS. Although it retains most of the features of CVS, including all of the networking support, it is more capable than CVS, and easier to use.
Mark Pilgrim:
PyTextile is a Python port of Textile, Dean Allen’s Humane Web Text Generator. It supports all the features of the original, with the exception of converting high-bit characters to their HTML numeric entity equivalent, because that’s some bad-ass PHP goin’ on and I don’t pretend to understand it.
By the way, Pilgrim’s Dive Into Python is fabulous.