Name: Keith Rarick
Member since: 2008-06-26 23:46:54
Last Login: 2009-03-31 21:03:59
Homepage: http://xph.us/
Notes:
My github page is at github.com/kr.
Don’t Copy the Call Stack
Some runtimes claim to provide first-class continuations, but implement this by copying the entire call stack. This implementation strategy makes continuations totally unusable in production code, and it should be outlawed. Or maybe such runtimes should be required to call them “shitty continuations” instead of just “continuations”.
What to Look For in a Programming Language
Occasionally, I get asked what I look for in a programming language, what makes a good language, or what I would do to improve an existing language. Mainstream programming languages (which are almost always applicative with call-by-value evaluation) vary surprisingly little in their abilities, but there are a few significant differences. Aside from all the usual, obvious aspects that don’t need to be repeated, I look for two big things:
These features are even more useful when combined, yet I’ve never seen a language with both. (So, to my knowledge, sodium will be the first!)
Asynchronous Programming in Python
Twisted is pretty good. It sits as one of the top networking libraries in Python, and with good reason. It is properly asynchronous, flexible, and mature. But it also has some pretty serious flaws that make it harder than necessary for programmers to use.
This hinders adoption of Twisted, and (worse) it hinders adoption of asynchronous programming in general. Unfortunately, fixing most of these flaws in the context of Twisted would cause massive compatibility problems. This makes me think the world could use a new, Pythonic, asynchronous programming library. Perhaps it should be a fork of Twisted; perhaps it should be a brand-new project. Either way, it would make life much nicer for programmers like you and me in the future. <h2 id='toward_a_better_event_library'>Toward A Better Event Library</h2>
Here is what Twisted gets right:
These things are all absolutely crucial and Twisted nails them. This is what makes Twisted so great.
Here is what I would do differently:
This last problem deserves special attention. The rest are mere annoyances and could be suffered through, if not for implicit chaining. It is a fundamental design flaw, and I wouldn’t be surprised to learn that it’s responsible for more bugs in Twisted-using programs than any other single factor.
Let me first spell out exactly what I mean here by “implicitly chained” callbacks and “simple” promises. In Twisted, you can write:
deferred = background_job()
deferred.addCallback(cb_1)
deferred.addCallback(cb_2)
deferred.addCallback(cb_3)
Each callback here will be given the return value of the previous callback. I’ll refer to that as implicit chaining.
Instead I advocate having the promise give each callback simply the same value – the original result of the background job. So I’ll call this a simple promise. (In these examples, I’ll use deferred for objects with implicit chaining and promise for simple promises.)
promise = background_job()
promise.addCallback(cb_a)
promise.addCallback(cb_b)
promise.addCallback(cb_c)
In this example, each callback will get the exact same value. Nothing that any one of them does can affect the others.
Simple promises are more general. The key is to have addCallback and its friends return a new promise for the result of the callback. With this feature, you can still chain callbacks, but you must do it explicitly. That is a good thing. Consider a deferred with implicit chaining:
def add4(n):
return n + 4
deferred = background_job()
deferred.addCallback(add4)
deferred.addCallback(log)
Supposing background_job supplies a value of 3, this example will log 7. We can just as easily do that without implicit chaining:
def add4(n):
return n + 4
promise = background_job()
promise2 = promise.addCallback(add4)
promise2.addCallback(log)
This also logs 7. Now let’s look at an example starting with a simple promise:
promise = background_job()
promise.addCallback(log)
promise.addCallback(log)
This logs 3, twice. But try doing this with implicit chaining. It can’t be expressed. (Yes, you could achieve the same output in many different ways, but here I’m concerned with the structure of control flow.)
More importantly, implicitly chained callbacks are confusing. You must pay careful attention to the order in which you add callbacks. They require complicated diagrams to explain how they behave. If you want to insert a new callback somewhere, you have to be extra careful when you do it, to ensure it goes in the right place in the chain. By contrast, if you want to insert a new callback somewhere with simple promises, you have only to stick it on the correct promise.
Further, implicit chaining makes you do extra work to propagate return values and errors, even when your callback properly doesn’t care about such things. Let’s say you have the following snippet (which is the same for either a promise or a deferred):
either = background_job()
either.addCallbacks(my_result, my_error_handler)
You just want some basic logging to check what’s going on. With a simple promise, that’s easy:
promise = background_job()
promise.addCallback(log)
promise.addCallbacks(my_result, my_error_handler)
With implicit chaining, it’s more work:
def safe_identity_log(x):
try:
log(x)
# If log raises an exception, we still
# want our real callback to fire, so we
# have to catch everything here, even
# though that has nothing to do with the
# function of this callback.
except:
pass
# Likewise, we must take care to return
# the original value, or else the
# callback will just get None.
return x
deferred = background_job()
deferred.addCallback(safe_identity_log)
deferred.addCallbacks(my_result, my_error_handler)
<h2 id='this_post_is_too_long'>This Post is Too Long</h2>
Anyway. That’s all I got. I really want to see this exist. So badly, I might actually do it myself. But it will have to wait a bit. <h2 id='addendum_coroutines_and_continuations'>Addendum: Coroutines and Continuations</h2>
Writing good code in most asynchronous systems (including Twisted, node.js, and even E) feels inside-out. Your results are passed in as parameters; they don’t come out as return values like they normally would. Same for exceptions. This results in more verbosity, and it just feels weird.
My earlier post The Wormhole describes a transformation that turns things right-side out again. (It’s built out of continuations, but it could just as well be done with coroutines, say in Python.) It makes writing correct asynchronous code almost as easy as writing correct synchronous code. However, it can only be done correctly if your promises are of the simple variety. I’ve since learned that Twisted has attempted this trick. That implementation is useful, but it has several sharp corners. For example, this will not do what you would hope:
background_deferred = None
can_background_job_complete = False
@deferred.inlineCallbacks
def f():
global background_deferred
background_deferred = background_job()
value = yield background_deferred
returnValue(value + 4)
final_deferred = f()
background_deferred.addCallback(log)
final_deferred.addCallback(log)
can_background_job_complete = True
Supposing background_job supplies 3, what will this log? In real life: None, then 7. If these were simple promises, it would log 3, then 7.
Projects I Want
Some software I’d love to see get made, but that I don’t have time to write myself. <h2 id='couchdb_url_wrapper'>CouchDB URL Wrapper</h2>
CouchDB’s interface is almost good enough to expose directly to the public, but its URLs are ugly and crufty. See “URL as UI” and “Cool URIs don’t change” for some philosophy.
I’d like to see a thin wrapper that lets you design arbitrary clean URLs for CouchDB. It needn’t do anything else. <h2 id='face_detection_in_javascript'>Face Detection in Javascript</h2>
When I upload photos to Facebook, why to I have to locate the faces by hand? Why doesn’t Facebook do it for me? Javascript (in modern browsers) is now fast enough to do real face detection.
Many other web sites could benefit from this, too. There should be a free, high-quality Javascript library for face detection. At vasc.ri.cmu.edu/NNFaceDetector/ you can find papers describing a pretty good algorithm. <h2 id='face_recognition_in_javascript'>Face Recognition in Javascript</h2>
As an extension of the above, try to identify the people whose faces have been detected. Recognition is less generally-applicable than detection, but certainly Facebook-like sites can benefit from this. They could completely automate the process of tagging photos. Leave humans to identify the missing faces and false matches. <h2 id='docteststyle_tool_for_network_protocols'>Doctest-Style Tool for Network Protocols</h2>
I think doctest is pretty nice. I want a tool much like doctest, but for network conversations rather than python interpreter conversations.
So you could write something like:
>>> GET / HTTP/1.1
>>> Server: localhost
>>>
HTTP/1.1 200 OK
Content-type: text/plain
Content-Length: 6
hello
and this tool will check that your new web server is working.
File Management Ideas
In theory, Gnome Zetigeist makes me happy; kudos to the people working on it for daring to make something new. Its concept excites me, but its design specifics leave me wanting. I think we can do better, and here are some concrete design ideas to back up my claim.
First I want to recognize that the iTunes-style (or Rhythmbox-style or Banshee-style) interface is successful at mitigating the complexity of a huge collection of files. There are just a few salient aspects of such an interface: a list of “data sources” on the left (including “playlists” and “smart playlists”) and a data display on the right. The data display is usually, but not always, a scrolling table with sortable columns. It is also searchable and the results update instantly (sub-250ms or, ideally, before the next frame).
This interface should be applied to all user files, not just media. I don’t advocate an auto-generated interface. Rather each data source (each item in the blue side bar) must be thoughtfully designed. Here are a few examples. <h2 id='media'>Media</h2>
For the Music, Movies, and TV Shows, start with an exact copy of iTunes. It’s pretty good. Then go ahead and make changes if you wish, but your changes must actually be better, not merely different. <h2 id='documents'>Documents</h2>
The Documents, Spreadsheets, and Presentations items would simply give appropriately-filtered lists of files. <h2 id='downloads'>Downloads</h2>
I’m staring at three windows. The first one is drawn by Firefox; it shows a list of files I am currently downloading. The second is drawn by Nautilus; it shows a list of files I have recently downloaded. The third is drawn by Transmission; it shows a list of files I am currently downloading with BitTorrent.
Most of the time I do not care about these distinctions. I simply want to get the file I just downloaded. If it hasn’t finished yet, tell me so. I do not know, a priori, if it has finished downloading – that’s part of why I want to see it.
The Downloads item should present a list of files that have been or are being downloaded (in reverse chronological order by default). It should handle HTTP, FTP, BitTorrent, and all other things (hello, plugins…) that conceptually allow you to “download” a file. <h2 id='bookmarks'>Bookmarks</h2>
Firefox lets me make bookmarks of web pages. Gnome lets me make bookmarks of things on my computer. Again, I mostly do not care about this distinction. Any open window that represents a file should let me make a bookmark. The window could be showing a spreadsheet, video, web page, mail message, or something else. I don’t care. Just give me a little star button like in Gmail. The star should probably be in the window title bar. (I think this would require an EWMH extension.)
The Bookmarks item should then present a list of things I have starred.
It should also understand Delicious, Weave, and similar things (hello, plugins…). <h2 id='history'>History</h2>
Gnome keeps a list of recently opened files. Firefox keeps a list of recently visited web pages. You guessed it; I don’t often care about that distinction. The actual data display should be at least as useful as what you get in a modern web browser. <h2 id='trash'>Trash</h2>
Trash is relatively prosaic, but I’ll go out on a limb and suggest that items in the Trash should be deleted automatically after 30 days, or when space is needed. I should not have to empty the Trash myself. <h2 id='photos'>Photos</h2>
This one is interesting because it probably makes most sense to show thumbnail images rather than a table of text. Sort of like F-Spot. <h2 id='preferences__applications'>Preferences & Applications</h2>
These aren’t in my mockup, but might make sense to include. <style> .right { float: right; width: 230px; margin: 0 0 24px 15px; padding: none; } </style>
Others have certified kr as follows:
[ Certification disabled because you're not logged in. ]
FOAF updates: Trust rankings are now exported, making the data available to other users and websites. An external FOAF URI has been added, allowing users to link to an additional FOAF file.
Keep up with the latest Advogato features by reading the Advogato status blog.
If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!