I recently implemented a feel-good bit of functionality with my awesome window manager. I figured out how to use the builtin popup menu to drive the uzbl browser. In doing so, I thought I understood how it worked. To be thorough; however, I decided to do an investigation. I learned that my original understanding was flawed and that I had inadvertantly stumbled across a software concept I’d heard of but never properly understood- the closure.
I’d never really striven to understand closures properly because I categorized them as ‘software theory.’ They never seemed like the sort of thing that I could use practically in the software I worked on. Admittedly, this thinking is a fault of mine. But I’ll defend it by noting that I think I end up with a deeper understanding of concepts than the average because I often times will implement things and then go back to understand why it worked so well. Many times, it turns out I used a bit of software theory without realizing it. One might counter that if I’d understood the theory to begin with, I could have saved myself some trouble. That’s likely correct, and my, admittedly feeble, reply is not all of us learn the same way.
So, here’s how I learned to understand closures.
First, the background: I wanted to be able to drive an existing uzbl-browser instance using an awesome popup menu. The sequence consists of a key command from uzbl triggering the popup menu, followed by the menu selection then driving uzbl to a new url. The awesome menu structure is very simple, consisting of a text field for the menu entry and then a corresponding action. The trick here is that uzbl communicates to external programs using a socket, so I had to tell awesome what socket that particular instance was using when the menu is created. Thus, the menu had to be dynamic; I couldn’t just build a static popup and then invoke it because the command associated with the menu item has to change each time the popup is run.
With that in mind, here’s the relevant piece of (lua) code:
function webmenu(sock_f)
local wwwmenu_t = {}
.
.
.
for line in io.lines(path .. "/uzbl/bookmarks") do
local site, url = string.match(line, "^\"(.+)\"%s+\"(.+)\"$")
table.insert(wwwmenu_t,
{ site, function()
send_to_uzbl(sock_f, "uri "..url)
end })
end
return awful.menu.new({ items = wwwmenu_t})
end
The closure is created with the anonymous function in the table. It makes reference to the sock_f
parameter and the url
value, both of which are created and assigned outside the context of the anonymous function. In closure terminology, these are the ‘upvalues.’ When I originally implemented the function, I actually used locals inside the anonymous function like so:
function()
local sf = sock_f
local cmd = "uri "..url
send_to_uzbl(sf, cmd)
end
My thought was that the scoping rules would enable the code to work. The code did work of course; but its working didn’t have anything to do with scoping rules. I sort of realized that I didn’t totally understand what was going on, so I started to investigate so I could understand why it worked. After I understood the closure, I realized that I could simplify the anonymous function into the code in the first snippet.
While it’s a useful technique, it has it’s costs. Since I’m using a table object, the code is essentially creating a different function context for each entry in the table. Clearly, a little memory intensive. In order to combat this, I structured the surrounding code such that the table and popup menu object are garbage collected after they’ve been used.
Another cost is the fact that the programming language has to be a high-level language. The C language, for instance, cannot create closures via compilation. Though this is hardly surprising, since C really can only support what the underlying hardware can do. A closure is a software concept, not a hardware concept. I’m sure something could be crafted in C to duplicate closure functionality. Lua is, after all, implemented in C and it clearly supports closures. Python, scheme, and a number of other languages support them as well.
I enjoy solving problems. This particular one I had to noodle a little. To my pleasant surprise, the solution gave me a little more than I bargained for. Yes, it enabled me to implement functionality I wanted; but it also gave me a valuable lesson in software theory. Another tool to work with for future problems is always a welcome acquistion.
One reply on “Closures”
…too bad yu can’t use it on the boy and lass at bed time …