Wednesday, March 17, 2010

Implementations of Slideshow in C# and Clojure

As I promised I would the other day, I went ahead and implemented my stupid little slideshow app in both C# and Clojure. It was an interesting exercise, the more so because I’m not entirely sure what conclusions to draw. I may have more to say about this at some point in the future, but for now I thought I’d just put up the code and share some general observations. So, you can find the C# version here, and the Clojure version here


The app is pretty simple. It starts a low-priority thread that lazily populates a list of filenames that are descendants of a particular directory. Meanwhile, a timer procedure randomly selects one of the filenames out of the ones that have been found so far and invalidates the screen. The repaint simply draws the image centered on a fullscreen window with a black background. If no images have been found yet, then it prints “Working…” on the screen instead.


The Clojure version isn’t really an app, because I wrote all this code really quickly and didn’t bother giving it a main() function, but other than that they’re about as similar as I could make them, given that one is running against Swing on the JVM and the other against Windows Forms on the CLR. I suppose I could have written the Clojure one against Windows Forms as well using ClojureCLR, but honestly it was simply more interesting to me to use Swing, as I’m not familiar with it. And this is anything but a scientific experiment.


For the C# version, I eschewed the Forms designer to keep it closer to the spirit of the Clojure one. And for something like this, where I’m just blasting raw pixels at the screen, I think that was a reasonable choice.


I wrote the Clojure version first. Perhaps that was a bad choice, since it meant I spent a lot more time on the Clojure one, since I had to figure out all the little details, like how to center an image in a region. It certainly made the C# version a lot easier to write, since I was pretty much just translating. But the C# version probably would have been easier for to write in any event, since I’ve been writing C# professionally for almost 10 years, and Clojure in my spare time for less than two.


One thing that surprised me a bit is that the Clojure version turned out to be not much shorter than the C# version. Indeed, if I had put closing braces on the same line as the statement they close (which is like the Clojure style I use), I’d bet they’re about the same. I had expected it to be quite a bit more compact, since, as I’ve said before, my experience with Clojure has been that it generally lets you be more concise. In this case, I suspect it has to do with the fact that I had a lot of Swing-related code, which doesn’t lend itself strongly to the sorts of concision that Clojure supports. Or it could be the fact that I’m a Clojure n00b, and just don’t know how to structure the code to hit a good balance of terseness with readability. Certainly, I could make it a bit shorter by inlining calls to things like make-frame. Other recommendations as to how better to write either version would be more than welcome.


Both versions of the code are pretty rough, as I wrote them essentially while I was waiting for stuff to install on a VM. I’d definitely clean them up more if I were going to do anything with them, but I figured it was better to get it up here than to wait potentially forever to get it polished. Really, I’m just trying to point out that not all my code looks this crappy. :)


You’ll note that in the Clojure version, I use an atom to store the list of filenames. That’s not because I’m taking advantage of Clojure’s awesome concurrency support, it’s because it was a convenient thing to do. Note that the C# version doesn’t bother with synchronization at all, since I’ve only got two threads, and one is only reading, and one is only appending. If I wanted to get fancier with the threads (e.g. have another thread doing look-ahead loading of the next image), then I might have to start doing synchronization. I’d be in good shape on the Clojure side then, since not much would have to change, and the fact that Clojure values are immutable really helps. I don’t think the C# version wouldn’t need too much work, but I’d have to think about it more to be sure, and it would depend on exactly what I was trying to do.


It’s apropos of nothing, but it made me smile that the C# version required PInvoke to get a fullscreen window, and the Swing one just supported it out-of-the-box.


Being familiar with Visual Studio was a definite benefit when working with the C# code. I’m not very facile with the debugging capabilities of Emacs/SLIME, which is what I use to develop Clojure, and when I had problems it took me a little longer to figure them out. That said, having the REPL was really awesome, and it really makes me hate the Immediate Window in Visual Studio by comparison. It would be interesting to try some of the other Clojure IDEs to see how they stack up.


Neither version of the code has much in the way of comments. Sorry about that. :)


Anyway, that’s my pile of unstructured thoughts. Hope it’s of interest, if not value, to at least some of you. And as I said, further discussion, rewrites, comments, and criticism are wholly welcome.




Wednesday, March 10, 2010

Creating a Lazy Enumeration of Directory Descendants in C#

Update: it turns out that Clojure has a built-in file-seq function (thanks to Stuart Sierra for pointing this out) that already does exactly this, making Clojure the winner hands-down for brevity :) Seriously though, arguments about “batteries included” aside – although a valid point of consideration – I still think it’s interesting to consider the bits of code below.

Yesterday, I posted some Clojure code that showed how to create a function that would lazily return all the descendants of a directory. Here’s the code again:

(import [java.io File])

(defn dir-descendants [dir]
(let [children (.listFiles (File. dir))]
(lazy-cat
(map (memfn getPath) (filter (memfn isFile) children))
(mapcat dir-descendants
(map (memfn getPath) (filter (memfn isDirectory) children))))))


Towards the end of the post, I tossed off a somewhat off-hand comment:




Two things struck me about writing this code. The first is that it’s really, really compact. It would be even smaller if it weren’t for the calls to memfn, which is just there so I can use a Java method as if it were a Clojure function. The second is that it was a bit of a mind-bender to write, although having written it I find it fairly easy to read. But I suspect both of those things would also be true if I attempted to write the equivalent C# (including the laziness, probably via a “yield return” implementation of IEnumerable somewhere), although it would almost certainly be at least somewhat more verbose.




Well, Chris Sells called me on that one. Here’s the equivalent C# code he came up with:



using System.IO;
using System.Collections.Generic;

namespace ConsoleApplication1 {
class Program {
static IEnumerable GetDirectoryDecendants(string path) {
foreach (var file in Directory.GetFiles(path)) { yield return file; }
foreach (var directory in Directory.GetDirectories(path)) {
foreach (var file in GetDirectoryDecendants(directory)) { yield return file; }
}
}
}
}


I think quantitative comparisons of code terseness are generally pretty silly, but - subjectively - when I look at that it seems just as compact as the Clojure version. So I stand corrected on that one. A few other observations:




  • Chris included namespace and class statements. Clojure has equivalents for these, but I omitted them. Omitting them from the C# version would make it even shorter.


  • The C# version can actually be made more lazy than the Clojure one because of the CLR’s Directory.EnumerateFiles method, as pointed out by John in the comments to yesterday’s post. That’s pretty cool. I could, however, take advantage of this if I used ClojureCLR instead of the JVM-based Clojure.


  • I said that writing the Clojure version was a bit of a mind-bender. To clarify, the bendiness came from the behavior of lazy-cat, and I think primarily because I’m relatively new to Clojure. The “yield return” in the C# code – which is the key to the laziness – gave me similar pause when I first encountered it years ago. And although I’m comfortable enough with it now, the two yield return statements in Chris’s code made me pause for a moment to convince myself it would work without consuming stack. So I think there’s probably some inherent complexity in the laziness that – like many things – is no big barrier once you internalize it in either language.



So I haven’t proven anything to anyone, not even myself. That said, having worked with Clojure a bit and C# a lot, I’m still left with the impression that Clojure is generally more compact. It will be a few years before I can say whether that’s a good thing or not (or even whether it’s true), since I tend to think working with a language on that time scale is the only way to really get to know it. I will do two things, however. The first is to show you another bit of code in both Clojure and C#. First, the C#:



private static IEnumerable Fibs()
{
yield return 0;
yield return 1;

int a = 0;
int b = 1;
while (true)
{
int temp = a + b;
a = b;
b = temp;
yield return b;
}
}


Next, the Clojure:



(def fibs (lazy-cat [0 1] (map + fibs (rest fibs))))


Hopefully it’s obvious from at least one of these that they’re meant to compute the Fibonacci sequence.



Maybe someone can come up with ways to make the C# more compact: I would welcome such suggestions. And I’m not trying to have a Clojure-versus-C# contest here – those are stupid, like saying, “Which is better: a hammer or a drill press?” But language comparisons are still interesting in that you generally wind up learning something you didn’t know about at least one of the languages.



The other thing I’m going to do is to implement my slideshow app (the thing that had me writing the directory recursion function in the first place) in both languages and post them here. Until Chris posted, I was pretty sure I knew how they’d stack up. Now I’m just curious! Maybe you are, too.



Thanks, Chris, for keeping me honest.

Tuesday, March 9, 2010

Creating a Lazy Sequence of Directory Descendants in Clojure

Update: it turns out Clojure has a built-in function file-seq that does (almost) exactly this. The difference is that you get a lazy sequence of File objects, not paths. Hooray for "batteries included"!


I was playing around with Clojure this morning, attempting to write a little photo viewer application. As part of this, I wound up writing a function that produces a lazy sequence of all the files that are descendants of a given directory. I was pretty happy with how it turned out, so I thought I’d share it. Here’s the code in its entirety: 



(import [java.io File])

(defn dir-descendants [dir]
(let [children (.listFiles (File. dir))]
(lazy-cat
(map (memfn getPath) (filter (memfn isFile) children))
(mapcat dir-descendants
(map (memfn getPath) (filter (memfn isDirectory) children))))))



The key bit here is the lazy-cat, which returns a lazy sequence – it doesn’t evaluate the second expression (in which I walk into the children of the current directory) until someone asks for the next element in the sequence. In other words, even though this function looks recursive, it’s not. Because lazy-cat returns right away, the “nested” call to  dir-descendants happens after the stack has been popped. This is totally awesome, because it means I can call dir-descendants and it’ll return right away. So I can start displaying pictures without having to wait for the entire directory tree to be enumerated. 


Two things struck me about writing this code. The first is that it’s really, really compact. It would be even smaller if it weren’t for the calls to memfn, which is just there so I can use a Java method as if it were a Clojure function. The second is that it was a bit of a mind-bender to write, although having written it I find it fairly easy to read. But I suspect both of those things would also be true if I attempted to write the equivalent C# (including the laziness, probably via a “yield return” implementation of IEnumerable somewhere), although it would almost certainly be at least somewhat more verbose.