Archive for the 'Programming' Category


On writing well (on the web)

2008-02-20 / 16:43 / dave

Steve Yegge’s posts always generate discussion. Unfortunately a lot of it looks like this:

he’s still a 2yo noob at writing, pointless overexplanation of every detail of his thought process. someday he’ll learn how adults write.

– gsw07a, comment on Reddit

I don’t know how many of these comments Steve endures, but apparently enough that he wrote an article about why his long posts are popular. Conclusion: 1) because he’s a rebel and 2) it flushes your cache. Interesting, but I think he’s missing a key point: he’s entertaining. I mean “we have the Quality With a Name — namely, Suckiness.”? That’s just golden.

Most complaints point out that he could say the same thing in fewer words. I don’t doubt he could convey the same message–say, “static typing is dumb”–in fewer characters, but it wouldn’t have the same voice. Stevey is in no rush to get to the end and has a penchance for exaggeration. It’s like talking about programming with your friends. At a bar. If he were to have 15 pages of, say:

Static typing is inferior to dynamic typing for the following reasons:
1. Static typing requires system restart to change types
2. Static requires up-front design to avoid conflicting with the type-checker
3. Static typing is not a replacement for unit testing

It would be like having a discussion with that guy at work that no one really likes. That guy doesn’t have any friends which is why he spends his weekends memorizing the digits of pi.

Don’t be that guy

It’s easy to read blog posts and evaluate them as though they are computer programs[1]. They accomplish a task: teaching you something new. If that task can be accomplished in fewer tokens… great! Except reducing them to, say “don’t use static typing” is reductio ad absurdum. Unless you’re googling for “should I use static typing” this is pretty useless; it’s a simple recipe that teaches you nothing. Good teachers move beyond simple instruction to providing insight.

Great teachers are also entertaining. But being entertaining is hard. Thinking that you’re entertaining is often inversely correlated with success[2]. There’s also a thin line between entertaining and annoying, so if your style has a high variance you’re likely to just end up pissing-off a bunch of people.

The classic way to entertain with text is to be a good writer. Unfortunately, that’s hard to do: it takes lots of time to become a good writer and even more time to edit each work. And failing at writing can be worse than not trying: you end up boring and pompous.

The easy fall back is the one Steve adopts: use your own voice. At the very least it makes your writing authentic.

[1]: Well, if you’re a computer person.
[2]: Which explains this blog.

A tidbit on writing, the internet, and Paul Graham

Paul Graham seems to think his essays are delivered as informally as possible which makes me think he doesn’t read many other blogs and has never, ever been to something awful.


Programming with the old guard

2008-02-17 / 21:57 / dave

I’ve finished going through all the OOPSLA 2007 podcasts, and was struck by the keynotes from a pair of CS classics. David Parnas talked about the need for documentation while Frederick Brooks talked about collaboration and conceptual integrity. Parnas’ talk is clearly against the current hip “we don’t document because we write clean code” meme. Brooks’ love of conceptually integrity is uncontroversial. But he reiterates an idea he mentions in The Mythical Man Month, a development team with a clear leader, which is against the agile idea of a flat team that delegates design to individual coders (with design reviews, etc. for quality control).

It’s interesting, and kind of nice, hearing people I respect diss a development methodology I enjoy. My first reaction was “what?!”, which was a clear sign that I hadn’t been thinking critically about Agile for awhile. I’m also curious how their views are due to their age, or more specifically due to their history. Parnas’ quotes frequently from his work on airplanes for the Navy and the Airforce while Brooks is known for IBM’s OS/360. Both are the massive systems that are difficult–well, or incredibly easy, depending on who you ask–with Agile. Likewise, while both Parnas and Barnes state that they’re anti-waterfall, I wonder how they would imagine the development of something like a 37 Signals web app.

And then there’s John McCarthy…

…whose discussion of Elephant 2000 was complex enough that the only question was of the “I’m smart and know of this related research… do you?” format. In the spare time left over he went through a history of Lisp. Everyone loved that part.

So is John McCarthy bad at explaining things, or is he still so far ahead of his time that people will be talking about Elephant at OOPSLA 2057?


Avoiding trolls

2008-02-17 / 21:17 / dave

Paul Graham has some thoughts on trolls. I agree with his explanation of why people troll (also check the original comment thread), but think he might be over-selling the future of News.YC. He thinks they don’t have many trolls because

  1. News.YC users don’t tolerate trolling
  2. Votes on comments affect your karma
  3. They’re small

It’s nice to think that #1–the noble intention of the users–somehow matters, but in my experience it doesn’t. I’m just a lurker (check that bangin’ karma!), but I don’t think there is anything particularly anti-troll about the News.YC’s users. I’ve traveled from Slashdot to Digg to reddit and can say that none of them seemed troll-friendly. For a time, most of reddit’s stories seemed to be about how Digg was crawling with trolls while reddit was just peachy. Three months later…

Having comments affecting your karma (#2) seems like a good idea. It emphasizes good discussion rather than skill at hunting down pro Ron Paul stories or speed at posting xkcd’s latest. Of course, this only matters if trolls care about karma. Someone with nothing better to do than post drivel in forums probably doesn’t mind burning down their karma and starting a new account. It also puts faith in the wisdom of the crowds. Paul has observed that News.YC’s algorithm hasn’t resulted in the down-voting of unpopular ideas. Part of that is probably based on another part of the algorithm: only users with karma of 20 or greater can down-vote. This leaves the judgement in the hands of the “better” users.

Rapid growth makes companies stupid. Part of this is probably bureaucratic drag, but there is also the failure of hiring: B players hire C players. Similarly, as they grow, forums attract less concerned users. For most forums, “hiring” is “whomever can fill out a sign-up form”. News.YC’s 20-karma rule is an improvement, but the ability for anyone to up-vote ensures that some B’s and C’s will make it to 20-karma status.

All of this makes me think that the main effect is due to size (#3).

All is not lost (or “So, you want a forum that doesn’t suck”)

If you believe John Gabriel’s Greater Internet FuFartwad Theory [NSFW, language], you can avoid trolls by:

  1. Having a small audience
  2. Disallowing anonymity
  3. Attracting better than average users

The best example I know of is the most excellent Lambda: the Ultimate (L:tU). It’s a small group of extremely bright people. Anonymity is discouraged, so the majority of contributors use their real names and provide links back to their real web sites. Karma therefore is not a number in the upper-right hand corner, it’s public opinion of your intelligence. This seems to dissuade trolls. There are still minor (and intelligent) flame wars and joke comments, but overall the discussion is good. Real good. Other special purpose sites, such as the Clojure group or Paul’s own Arc forums are examples where the topic has refined the user base.

Small size isn’t for everyone, however. If you’re making money from ads (and want to drive a Jaguar), you probably want as large an audience as possible; if you’re selling your site on conversation you need a critical mass before it’s worthwhile. This makes growth a delicate game: too many users and your Digg, too few and you’re… well, you’re this blog. Though I do appreciate my audience: you are the four smartest people I know.

A strong leader

News.YC has another thing going for it: Paul Graham. He has a strong influence: his posts are always up-voted, his comments sway the conversation, and, to be honest, more than a few users are there because they respect what he’s done. Similarly Ehud Lamm (L:tU user #1 in case you mised it in the URL) is an active presence on L:tU and occasionally reminds users of the rules. Paul and Ehud are sharks in the water: their presence reminds posters that they are being watched. This is the intended effect of the News.YC’s 20-karma rule, just with way less scary sharks.

On the other hand, CmdrTaco was always present on Slashdot, and that didn’t stop it’s slide into mediocrity. That may have something to do with the tone. CmdTaco, god love him, is not the most serious cat. Even the name Slashdot was a joke. By contrast Paul and Ehud, while not crumudgeons, get down to business.

Bringing the power back to the (smart) people

The saying about B players hiring C players supposedly came from Apple. Apple is a good example of a company that is large but not stupid (anymore). This non-stupidity has a name: Steve Jobs. He’s a strong leader if ever there was one. But relying on a single leader also has it’s flaws. Do you need one?

Another company surviving incredible growth is Google. Although Larry and Sergi are smart guys, they seem more hands-off than Jobs. According to those at Google, it’s a very flat hierarchy, Google is relying on the genius of it’s crowds. And the crowd is pretty genius, thanks to Apple’s advice: Google’s interviews are tough. A flat hierarchy also makes it harder to hide in the corner of an org chart. If you’re not producing, people will know (this is also the case at small companies: everyone knows each other, so your stinky ideas are easily uncovered).

If you meet an idiot, don’t let him tell you that you suck

Can Google’s experience be applied to forums? All you need is a way to eliminate anonymity and rareify your users. I propose the answer is authorship.

For awhile, several bathrooms I used had books of zen sayings. One was something like

If you meet someone who is not a samurai, do not show him your sword.

My first read was “don’t cut up unarmed civilians,” which is certainly good advice. My second take was “someone who isn’t a samurai will not appreciate the quality of your sword.”

Trolls don’t contributed while telling everyone else how much they suck. Force people to contribute before they can comment (or vote), and maybe the problem goes away. Contributions could be a users own submitted blog posts or articles written on the site. This puts commenter’s skin in the game: they are creating an environment for evaluating their own work. It also makes for a very immediate form of karma: don’t piss off the people who are judging you.

This also effectively eliminates anonymity. I don’t mean anonymity as in hiding your “real-world” identity, but hiding your reputation behind a disposable identity. A non-disposable pseudonym works well, see chromatic for a good example. Combined with a karma system, the system could even allow for cloaked comments: a user posts sensitive items anonymously but the system applies moderation karma against their real identity. A downside is that cloaking is not legally anonymous: identity is discoverable via subpoena.

Another lesson from Google is it’s secret sauce: PageRank. When I started this post, I was mainly thinking of PageRank in terms of it’s technical implementation: high karma users votes carry more weight, etc. But it sounds like Slashdot already approximates that. If there are lessons in PageRank, they are 1) people try to game the system and 2) you’re going to need to tweak your algorithm.

So what about News.YC?

Oh, right.

I think News.YC’s best bet is simple: don’t try to grow.

If growth is inevitable, surviving becomes a matter of constant evolution: can your “algorithm” (which might be more social, such as disallowing anonymity) outpace the trolls? Karma from comments and a 20-karma minimum to down-vote are good examples. There is a small risk changes will alienate old users (as in “my karma went from 4 billion down to just ‘Excellent’… WTF?”), but this is outweighted by the positive effects: better content and that warm fuzzy feeling of knowing that someone is working hard to keep things tidy.

It’d be nice if such a system could remain open to all contributions. My fear is that it cannot, and that requiring contribution (as described above) will be required. But this is just another option to try if quality declines.

So I don’t think Paul’s stated reasons (anti-troll users and current karma system) are enough, but I do think it’s possible. And success will rely on rapid iteration so–given Paul’s methodology–I’m hopeful.


Planned outage

2008-02-07 / 12:12 / dave

Due to continued space and power constraints in our primary data center,
we will be moving the “randy” cluster to one of our newer data centers.
This move will begin Friday, February 8, at 10PM PST, and is expected to
last up to 8 hours, until Saturday, February 9, 6AM PST. All web servers,
mail servers, file servers, and MySQL servers in the randy cluster will be
unreachable during this time.

Fear not, loyal readers! It is only 8 hours.


Arc: two more links in twenty words (including this title)

2008-02-01 / 09:55 / dave

  1. Arc might be too short to disambiguate
  2. Arc forums RSS

Arc: the blogosphere rages

2008-02-01 / 00:04 / dave

And, I’m sure, tons more. This isn’t rare–just see the title of Reg’s article–so why do I care? Probably because my own thoughts are summed up in this comment:

Here’s why Arc is great: because we’re all talking about it here and so is everyone else. It’s great because I am excited to try it. Can you remember the last time you felt that way about a dialect of Lisp? Was it recently?

For me, Lisp is interesting in an “old but neat” way, much like Forth or Joy. Python is interesting in a “useful every single day” way. Paul Graham’s advocacy of quick and dirty hacking holds promise of creating a language with the soul of Lisp/Scheme but with a pragmatic tilt, sort of the Python of the Lisp world. And his fame means people will pay attention. So while some other L:tU comments are quick to point out that Arc breaks no new ground, I think they miss the fact that Arc has the potential to bring Lisp to a newer, younger audience.

PS: if you’d rather have links that actually help you use Arc, here they are:

EDIT: fixed error in Aristotle’s title


Language design and Paul Graham’s dirty strings

2008-01-31 / 21:29 / dave

Well it’s finally happened: Arc’s out (also check out the Arc language page and the Arc tutorial). Paul Graham’s been an advocate of rapid prototyping*, but Aristotle Pagaltzis thinks he’s made a mistake by ignoring Unicode.

Getting character strings right, however, is something that you really do need to get right at the core language level. You cannot leave it for libraries to fix…

Leaving Unicode support in a language “for later” means you will spend a huge chunk of time sometime in the future to put it into the language – or you won’t, and then programs written in that language will forever be verbose when dealing with strings.

– Aristotle Pagaltzis, Paul Graham’s kind of dirty

I can’t speak on the importance of Unicode–my internationalization experience is putting <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/> in my header so I can post cool looking characters–but I am curious as to how, and more importantly when, Arc will get Unicode. My guess is “as soon as Paul Graham needs it.” So maybe if people request internationalized Hacker News comments or if he needs it to filter spam; or maybe someone else will need it and submit a patch,

Either way–waiting until it’s needed or waiting for a patch–is agile (”quick and dirty” as Paul calls it): don’t implement Unicode until you need it, and then pick only the implementation you need. It isn’t that different from other language evolution processes such as the JCP or Python’s PEPs. Python didn’t have Unicode until v1.6 (I think), and Unicode won’t be the native string representation until Python 3000. But it’d be hard to call either Java or Python’s evolution agile: “nearly forever” and “forever” are the times it takes to add a change and the intended lifetime of changes, respectively. Forever is not very agile.

One way to shorten the time is to ignore backwards compatibility. I think Aristotle misinterprets Paul’s Guido van Rossum parable: Paul is saying that Guido’s “year of Unicode” wasn’t spent implementing, it was spent thinking about backwards compatibility. In Arc… well, “we’ll change stuff without thinking about what it might break, and we won’t even keep track of the changes.” But there is an existing Arc user who will probably be unhappy if language changes slow him down: Paul “fast hacker” Graham himself. So I can’t imagine he’ll break things in a way that’s not easily fixed (say by prefixing your ASCII strings with a single b).

Which makes me wonder just how agile will handle language design. I believe in the general theory of agile, but there do seem to be times when thinking about the problem helps. Lots of elegant languages certianly seem to have been well-cogitated: Lisp, Smalltalk, Haskell, Erlang and REST (yes, I know it’s not a language). On the other hand, we have a language designed to get it done: Perl. Paul Graham has said that Perl is a hacker’s language, but I think it’s safe to say that Arc ends up as Perl-on-Scheme no one will be very impressed. (For what someone else has said about Perl as a hacker’s language, read Erik Naggum’s classic diatribe. PS: I used to write a lot of Perl.)

This is, of course, hindsight. I don’t know what went into the design of Lisp or Smalltalk. Was the elegant insight only derived after hundreds of ugly versions? For instance REST wasn’t designed, it is just a name pinned to the web’s best practices. Arc also isn’t trying to reinvent programming. In it’s current form, it’s a little syntax sprinkled on top of Scheme, which matches Paul’s claim that “the best kind of quick and dirty programs seem to be ones that are mathematically elegant, but missing features”: mathematically elegant = Scheme, missing features = everything not in Arc.

He also says that “good cleanness is a response to constraints imposed by the problem.” Aristotle’s problems require Unicode, and I doubt he’s alone. At some point Unicode support will show up. But when? And will it be elegant? And how much code will it break? Aristotle seems to think the answers are “too late”, “no” and “lots” while Paul’s answers are “when it’s needed”, “sure” and “who cares?”

Me? I’m content to wait and see.

*Though I guess you could say that waiting 6 years for your 1st release is a slow start.

EDIT: fixed typo in Aristotle’s title


Just use Emacs for everything

2008-01-22 / 09:38 / dave

…why is the Windows clipboard only capable of holding a single item?
– Jeff Atwood, Improving the Clipboard

It’s a mystery to me why none of the major operating systems have bothered improving the clipboard. It seems entirely possible to add these enhancements without breaking the simple clipboard paradigms that have been around since the days of Xerox PARC.
–Jeff Atwood, Reinventing the Clipboard

Just use Emacs!* I mean you can use it for everything. I guess that’s a bit hypocritical since I do my browsing in Firefox, but my browsing doesn’t involve that much pasting.

* Is there an easy to use acronym? Something like RTFM? JUE works, but it’s got a bad mnemonic.


Blogger and printing (and making it, you know, work)

2008-01-16 / 19:12 / dave

I was going to print David MacIver’s Why not Scala? article, but…

Printing from blogspot: before, page 1 (smaller)

Printing from blogspot: before, page 2 (smaller)

Well, Blogger, good job with the print style sheets. I guess that’s why we have Javascript and the lazy web. This stylesheet injection technique looks promising, and we can run the javascript via Greasemonkey or a bookmarklet. Dive Into Greasemonkey and the script loader that Western Civ used for MRI are all we need.

The files are all hosted on this site: http://svn.ndanger.org/blogger_print/:

Makefile
Automatically deploy live files (since can’t hotlink to SVN)
blogger_print.css
CSS stylesheet
blogger_print.url
Text file of URL for bookmarklet
blogger_print.user.js
Javascript for injecting CSS. Used by bookmarklet and Greasemonkey
readme.txt
Help file

I’ve been tweaking things when necessary, which is whenever I want to print from a Blogger site and think “god damn, why does the text flow off the right side of the page?”, but it’s still pretty rough: Greasemonkey only triggers on blogspot subdomians, some templates aren’t handled right, and the CSS contains some hacks to keep elements from overlapping too much (though no guarantees). If too many sites cause trouble I might go nuclear and remove all existing styles (right now it tries to preserve as much of the sites styling as possible) or maybe thermo-nuclear and strip out the sections I care about and rebuild the DOM.

Basically feel free to use it, but don’t expect it to work always, or even often. It works on stevey’s blog though, so feel free to join the comic fun.

Here’s the “after” print preview of David’s blog. Woo!

Printing from blogspot: after, page 1 (smaller)

Printing from blogspot: after, page 2 (smaller)


Error handling in Python: monads are too much for me

2008-01-16 / 14:01 / dave

Reading Peter Thatcher’s Monads in Python (with nice syntax!) had two effects: 1) it–along with Oliver Steel’s article and the Wikipedia–helped me understand what monads do and 2) it made me think monads were the bee’s knees for error handling.

But then I read A Little Lesson on Laziness and Unsafety and I remembered Reg’s post about how tail-recursion could result in debugging difficulties (well, if I remembered correctly since I couldn’t find the link). I also reviewed Peter’s code and it’s, to my un-haskelly brain, excessively magical (though that’s far from criticism: I thought the article was pretty amazing).

So then I started thinking about error handling and wrote some code… but Michael Feathers thinks we don’t talk about error handling enough, so first a few pages of drivel.

Out of bounds

Most modern error handling is done via exceptions. Exceptions are nice for one reason: they’re optional. You can blissfully code down the happy path and rely on the global handler to barf. Coupled with test driven development, it’s an efficient way to write code: break it, fix it (and eventually) upgrade it with error handling, though I use the term upgrade loosely. Joel Spolsky thinks–or at least thought in 2003–that exceptions are glorified gotos; basically exceptions are out-of-band signaling. Out of band signaling worked for the phone companies because they were trying to keep control out of the hands of their users. But since we’re our own users, we’d love to be able to keep signaling in band (so that we can, you know, build things like blue boxes).

But Joel’s preferred C/C++/Java example is… well…

T tmp;
if (ERROR == g(x, tmp))
     errorhandling;
if (ERROR == f(tmp, result))
     errorhandling;

Yikes! Verbose and still out of bounds: return has been subsumed for the status while output variables are used for the actual return value.

Bundling the status with the value helps. Although Erlang’s got exceptions [PDF], error handling was traditionally via tuples (note: IANAErlangExpert):

…the predominant way of signalling the success or failure of a function is to make it return tagged tuples like {ok, Value} in case of success and {error, Reason} otherwise, forcing the caller to check the result and either extract the value or handle the error.
– Carlsson, Gustavsson & Nyblom Erlang’s Exception Handling Revisited [PDF]

Now the problem–which is mentioned in the pdf along with a million other smart things–is that the complexity is baked in: the callee has to start off with tagged tuples (or break clients when it changes) and the caller has to always deal with a tuple. Erlang does have pattern matching, which almost makes dealing with tuples a feature, but it’s still an extra step. Maybe that’s why Erlang programming conventions recommend coding the happy path and letting errors fall through to the built-in logging.

Although I crapped all over Joel’s C code sample, I agree with his solution:

I think the reason programmers in C/C++/Java style languages have been attracted to exceptions is simply because the syntax does not have a concise way to call a function that returns multiple values, so it’s hard to write a function that either produces a return value or returns an error. (The only languages I have used extensively that do let you return multiple values nicely are ML and Haskell.)
– Joel Spolsky, Exceptions

Instead of two channels, use a single channel with some fancy controls to overload the data. And we shall call these controls Monads.

At least that’s my impression of Peter’s division example (let’s be honest, IANAHaskellExpert either). The Maybe monad allows us to specify that mdiv will return a result or an error. It also takes care of the plumbing: Nothing propagates through further calculations.

And now for something that doesn’t use monads at all

But as I said, monads makes me nervous. Primarily it’s because the code is different (and I am terrified of change), but it also has to do with debugging. Imperative code is easy to debug: add a few prints where it’s crashing. Functional constructs remove that, even in a language as non-functional (har har) as Python.

>>> [1.0/d for d in (1, 2, 0, 3)]
Traceback (most recent call last):
  File "", line 1, in <module>
ZeroDivisionError: float division

Ignoring the contrived example, how do you know what data is causing the failure? We could simply replace the 1.0/d with a function that checks its arguments and reports, but that’s pretty specialized. Exceptions, on the other hand, are general: if we’re looking for failures just look for exceptions.

def log_excp(f):
    def f2(*a, **ka):
        try: return f(*a, **ka)
        except Exception, e:
            print "Caught exception %s running %s(%s, %s)" \
                  % (e, f.__name__, a, ka)
            raise
    return f2
print [log_excp(lambda d: 1.0/d)(d) for d in (1, 2, 0, 3)]

Caught exception float division running <lambda>((0,), {})
Traceback (most recent call last):
  File "<stdin>", line 155, in <module>
  File "<stdin>", line 148, in f2
  File "<stdin>", line 155, in <lambda>
ZeroDivisionError: float division

If we want to make this more monadic, instead of just debugging, we could return a value to indicate an error, like the built-in None:

from traceback import format_exc
def log_excp_cont(f):
    def f2(*a, **ka):
        try: return f(*a, **ka)
        except Exception, e:
            print "Caught exception %s running %s(%s, %s)\n%s" \
                  % (e, f.__name__, a, ka, format_exc())
            return None
    return f2
print [log_excp_cont(lambda d: 1.0/d)(d) for d in (1, 2, 0, 3)]

Caught exception float division running <lambda>((0,), {})
Traceback (most recent call last):
  File "<stdin>", line 160, in f2
  File "<stdin>", line 166, in <lambda>
ZeroDivisionError: float division

[1.0, 0.5, None, 0.33333333333333331]

If we replicate Peter’s mdiv (renamed to sdiv–for safe div–here), we can see the error propagate:

@log_excp_cont
def sdiv(n, d):
    return float(n) / d

def with_sdiv():
    val1 = sdiv(2.0, 2.0)
    val2 = sdiv(3.0, 0.0)
    val3 = sdiv(val1, val2)
    return (val1, val2, val3)
print with_sdiv()

Caught exception float division running sdiv((3.0, 0.0), {})
Traceback (most recent call last):
  File "<stdin>", line 160, in f2
  File "<stdin>", line 171, in sdiv
ZeroDivisionError: float division

Caught exception unsupported operand type(s) for /: 'float' and 'NoneType' running sdiv((1.0, None), {})
Traceback (most recent call last):
  File "<stdin>", line 160, in f2
  File "<stdin>", line 171, in sdiv
TypeError: unsupported operand type(s) for /: 'float' and 'NoneType'

(1.0, None, None)

So we get None. But the exceptions differ between val2 and val3. The first is a ZeroDivisionError, the second a TypeError. It works because we’re looking for any Exception, but it is not perfect. A bigger problem with None is that it’s not always an error value. Most operations on None throw an exception, but bool(None) == False, for instance. We’ll look more at None’s replacement later, now let’s make this

A big pretentious framework

from traceback import format_exc

class excp_handler(object):
    def __init__(self, *mappings):
        if len(mappings) > 0: self.mappings = mappings
        else: self.mappings = ((Exception, none),)
    def __call__(self, f):
        def f2(*a, **ka):
            try: return f(*a, **ka)
            except Exception, e:
                for etype, ehandler in self.mappings:
                    if isinstance(e, etype):
                        return ehandler(func=f, args=a, kargs=ka, excp=e,
                                        trace=format_exc())
                raise   # If no handler, re-raise exception
        return f2

none = lambda *a, **ka: None

Ok, not that big. excp_handler is a decorator for handling exceptions (it could have been written as a regular function, but I prefer classes for longer decorators). If an exception occurs while running the wrapped function, it searches for a match and runs a corresponding handler. If no mappings are found, the error is re-raised. If no mappings are specified, it defaults to returning None on any exception. none is just a helper that always returns None (since that’s our current error type). In action:

print [excp_handler()(lambda d: 1.0/d)(d) for d in (1, 2, 0, 3)]
[1.0, 0.5, None, 0.33333333333333331]

If we want debugging information, we can add it to our handler, in this case via a wrapper:

def elog(ehandler):
    def eh2(func, args, kargs, excp, trace):
        print "Caught exception %s running %s(%s, %s)\n%s" \
              % (excp, func.__name__, args, kargs, trace)
        return ehandler(func, args, kargs, excp, trace)
    return eh2

print [excp_handler((Exception, elog(none)))(lambda d: 1.0/d)(d) for d in (1, 2, 0, 3)]

Caught exception float division running <lambda>((0,), {})
Traceback (most recent call last):
  File "<stdin>", line 52, in f2
  File "<stdin>", line 129, in <lambda>
ZeroDivisionError: float division

[1.0, 0.5, None, 0.33333333333333331]

The ability to specify an arbitrary handler has some additional advantages. First it can work around the problems of None: if you control the use of the data, you can write a handler to return whatever null object you want. Your FooBar class’ methods can return NullFooBar.

Second, handlers can raise wrapped exceptions. Michael Feathers’ article is about different zones of error-handling: the trusted-zone and the external interface. Sloppiness is tolerable in the trusted zone but the error handling of your external functions is another part of your interface. One common solution is to define your own exception hierarchy and wrap outgoing exceptions:

class CustomException(Exception): pass

def mk_raise_excp(e):
    def raise_excp(*a, **ka): raise e
    return raise_excp

print [excp_handler((Exception, mk_raise_excp(CustomException)))(lambda d: 1.0/d)(d)
       for d in (1, 2, 0, 3)]

Traceback (most recent call last):
  File "<stdin>", line 204, in <module>
  File "<stdin>", line 57, in f2
  File "<stdin>", line 199, in raise_excp
__main__.CustomException

Though we should wrap the original exception for reference, but that’s a solved prolem (and this post is getting too long).

This implementation has limitations, for example there’s no handling of cases that require finally. Right now I’m comfortable only using it for certain cases. In fact, that’s what I do: this grew from code to handle errors in the middle of a map reduce. I’m sure use will flush out more problems.

Monads & debugging

Peter’s original article wasn’t as much about error handling as it was “oh wow, it works!”. But it does make me wonder: how do you handle errors inside Haskell (e.g.)? Specifically:

  • How do you add error handling to happy path code?
  • What do you get in the way of debugging information?

If there are any functional programmers out there, I’d love to hear your thoughts.

For our next trick: fixing that whole None thing

Instead of returning None (or a different object each time), we really want a universal Failure object. It should obviously indicate an error, track its history (the original exception and stack trace) and respond to operations with another Failure I haven’t implemented anything, but I think a custom Exception–maybe FailureException in honor of monads–would work. Subclassing Exception indicates that it’s an error and also allows for processing inside of excp_handler. Tracking history can be done by storing the original exceptions innards (again see Ian Bicking’s post). return self in FailureException’s methods will propogate the error. Injecting the return self could be a task for __getattribute__ along with a whitelist of allowed functions.

But I haven’t tried writing any of that, and it’s time for lunch.


Joy: a higher order Forth (or a brief and meaningless look at a language you don’t care about)

2008-01-07 / 23:57 / dave

Today an announcement of JoyJ crossed reddit, so I decided to check out the language. Joy is “Forth’s functional cousin”. If you recall, Forth is a low-level stack-based language, almost a stack-based VM assembly language. In contrast Joy is a higher-level language. For example, whereas Forth uses [ and ] to do tricks switching between compiled and immediate mode, Joy uses hard brackets to to delimit lists and code. Note that it’s lists and code. Like Lisp, code is data and can be used as arguments for combinators.

Code & data

I installed the reference interpreter linked to from the homepage: joy.tar.gz. It compiled fine in cygwin. They also provide just the C code if you want to use your own compiler & linker (doesn’t that sound like fun). Once it’s built, you just invoke joy.exe and start playing around. If you want help with any of the functions, check the online manual.

Let’s take a constant (5), square it and add 1 (Italics are input):

5 dup * 1 + .
26

To translate:

  1. put 5 on the stack
  2. duplicate the 5 (now there are two 5’s on the stack)
  3. multiply the two fives and put 25 on the stack
  4. put a 1 on the stack
  5. add 25 and 1
  6. end the program and pop the stack

If we wanted to reuse the “square and add 1″ logic, we can make that code by wrapping it in hard brackets. Then we can play around with it like any other list (dup duplicates the top of the stack, I use it to so we can play with a copy of the [dup * 1 +] list while leaving the original intact):

[dup * 1 +]
dup size .
4
dup first .
dup
dup rest .
[* 1 +]

Or we can use the i combinator to apply it:

dup 5 swap i .
26

This code duplicates our [dup * 1 +] list, adds 5 to the stack, swaps them and then uses i to interpret the list [dup * 1 +] as code. And–ta-da!–26.

We can get fancier:

dup [[1 +] [id] ifinteger] map 5 swap i .
27

We duplicate our original [dup * 1 +] code, run it through map and then run it as before. map is a combinator that takes a list, runs a function on each element, and returns the result. In this case the list is [dup * 1 +]. The function is [[1 +] [id] ifinteger] which adds 1 to each integer and returns other values unchanged, i.e. map changes our initial code so instead of adding 1 we add 2.

We can also assign a name to our [dup * 1 +] code (note that the . in the DEFINE statement terminates the definition, it doesn’t pop the stack)

DEFINE sqrplus1 == dup * 1 + .
5 sqrplus1 .
26

Now we can use sqrplus1 like a built-in. Unfortunately I don’t know a way to go backwards: I can’t start from a defined word and get the list. This isn’t to say it’s impossible, just that I don’t know how to do it.

sqrplus1 [[1 +] [id] ifinteger] map .
run time error: one parameter needed for dup
[sqrplus1] [[1 +] [id] ifinteger] map .
[sqrplus1]

Combinators

Combinators, if you didn’t take the time to check the earlier wikipedia link, are just functions that take functions (I think there’s also a fancy combinatorial logic behind them, but I’m ignoring that). We already used three: i, ifinteger and map. Joy’s also got other standards like fold and filter.

ifinteger and it’s branching siblings are interesting. Instead of a standard “if…then…else”, Joy uses combinators that read the boolean to check and the different code branches from the stack. The combinator checks the condition and then executes whichever branch is appropriate. This reminds me of Smalltalk’s (and I think Ruby’s) “conditionals are messages to booleans”:

result := a > b
    ifTrue:[ 'greater' ]
    ifFalse:[ 'less' ]

Fancy recursion

Although you can do regular recursion with DEFINEd functions, the coolest combinators are for allowing anonymous recursion. From the tutorial:

A high proportion of recursively defined functions exhibit a very simple pattern: There is some test, the if-part, which determines whether the ground case obtains. If it does, then the non-recursive then-part is executed. Otherwise the recursive else-part has to be executed. In the else-part there is only one recursive call, and there can be something before the recursive call and something after the recursive call. It helps to think of the else-part to have two components, the else1-part before the recursive call, and the else2-part after the recursive call. This pattern is called linear recursion, and it occurs very frequently.

Joy has a useful device, the linrec combinator, which allows computation of anonymous functions that might have been defined recursively using a linear recursive pattern. Whereas the ifte combinator requires three quoted parameters, the linrec combinator requires four: an if-part, a then-part, an else1-part and an else2-part. For example, the factorial function could be computed by

        [null]  [succ]  [dup pred]  [*]  linrec

There is no need for a definition, the above program can be used directly.

Very frequently the if-part of a linear recursion tests for a simple base condition which depends on the type of the parameter. For numbers that condition tends to be being zero, for sets, strings and lists that condition tends to be being empty. The else1-part frequently makes the parameter smaller in some way. For numbers it decrements them, for sets, strings and lists it takes the rest.

Joy has another useful combinator which has the appropriate if-part and else1-part built in. This is the primrec combinator, which only has to be supplied with two quotation parameters, the (modified) then-part and the else2-part of linear recursion. For the factorial function the two quotation parameters are very simple:

        [1]  [*]  primrec

computes the factorial function. So, if one wanted to compute the list of factorial of a given list of numbers this can be done by either of the following:

        [ [null]  [succ]  [dup pred]  [*]  linrec ]   map
        [ [1]  [*]  primrec ]   map

The factorial of a number is the product of successive natural numbers up to the actual parameter. The following compute instead their sums and the sum of their squares:

        [0]  [+]  primrec
        [0]  [dup * +]  primrec

There are also combinators for tail recursion, binary recursion (quicksort), tree recursion (tree walking) as well as a few others. I’d love to be able to comment about how this compares with fixed point combinators, but reading that makes my head explode.

For comparison, let’s look at factorial across some languages:

Python:

def fact(n):
    if n <= 1: return 1
    return n * fact(n-1)
>>> fact(5)
120

Haskell:

fact :: Integer -> Integer
fact 0 = 1
fact n | n > 0 = n * fact(n-1)
Main> fac 5
120

Joy:

5 [1] [*] primrec .
120

Interacting with the outside world

Joy’s got get and put for simple IO, though get didn’t work inside my interpreter, but that could be a strange cygwin interaction. It can also open files, read command line args (via argc, argv), check your environmental variables, and call other processes.

I don’t really know when I would use it, but it’s kind of cute to be able to easily do this:

"pwd" system .
/home/dgingrich/src/joy1
"wc -l *.c *.h" system .
  3130 interp.c
   262 main.c
   198 scan.c
   344 utils.c
   185 globals.h
  4119 total

And yeah, that’s all the C code. There’s also Joy code that comes with the installation.

Wrapping up

Joy = Lisp + Forth. Or something like that. I don’t think I’m going to look at it any further, though I’m more inclined to than with Forth. Forth seemed fun but low-level. Joy seems fun and high-level, but with some quriks. In particular:

  • The stack makes parameters implicit. You have to read the code to see how many arguments will be popped (or use comment conventions like in Forth)
  • You spend a lot of time juggling variables on the stack.
  • Everything reads backwards
  • Non-google friendly name (half joking)

Looking back at the ifinteger, Joy and Smalltalk have a similar idea for branching, but the way it reads is much different. The Smalltalk says “here’s a condition, if true, else” vs. Joy’s “if true, else, condition.”

In the recursive examples, the Haskell looks the best to me. Python, while simple, has if plumbing inside. Joy’s claim to fame is that it’s short and can recurse even though anonymous. That’s nice, but the Haskell looks almost identical to the mathematical notation. Pretty handy for readability.

Just for kicks

DEFINE pi == [0] [swap dup 2 pow 1 swap / swap -1 swap 1 - pow * +] primrec 12 * sqrt .
500 pi .
gc - 132 nodes inspected, 122 nodes copied, clock: 1
3.14159
10000 pi .
gc - 5029 nodes inspected, 5020 nodes copied, clock: 1
gc - 8986 nodes inspected, 8977 nodes copied, clock: 1
gc - 10008 nodes inspected, 9999 nodes copied, clock: 1
     32 [main] joy 1568 _cygtls::handle_exceptions: Error while dumping state (probably corrupted stack)
Segmentation fault (core dumped)

On an unrelated note

It looks like Forth is being used in the OLPC XO-1. Neat.


Shooting for the 5%: reading more, writing more (code)

2008-01-04 / 14:10 / dave

An even more fascinating metric is this: 5% of programmers are 20x more productive than the other 95%. If this were a science, like it claims, we could figure out how to get everyone to the same level
– Bruce Eckel, The Mythical 5%

Good point, but how? Bruce’s advice, distilled, is something like:

  1. Create the best environment–tools, people, practices–you can
  2. Be analytical, reject fads
  3. Be diverse: don’t forget business, economics & people skills
  4. Be willing to change your mind

Despite getting a BS in both engineering and computer science, a big weakness of mine is #2. Not so much because I lack the skill to be analytical, but because I lack the patience. I have trouble turning my brain off of internet time. Why concentrate one thing when I can write code, while reading blog posts, while listening to podcasts (65% faster no less)? Another weakness is one that he doesn’t mention but which is mightily flogged in every other “how to be successful” piece: willingness to fail. I tend to be pretty inertial and fear of failure is one main reason.

Like most deadlines, I’m late with my new year’s resolutions. With an eye towards the 5%, here they are:

  1. Be analytical in my self-reflection: Track my life “bugs”, make some metrics pretty & shareable
  2. Practice patience by reading books instead of just crappy blog posts (mine included)
  3. Write more code even if it sucks: my fun a day is solving Project Euler problems. I’m only one behind!

You down with APP? Yeah you know me! (Oh wait, maybe not on Dreamhost)

2007-12-24 / 14:02 / dave

In a combined quest to edit this blog from Emacs and stay super-cool, I’ve been trying to get atompub working on Wordpress 2.3.1 hosted on Dreamhost.

Will it work? Will I master the Atompub standard? Am I typing this in the world’s best editor?

Spoiler: NO

Read on for the thrilling details…

All I need to do is get to the damn URL

I started with the Wordpress AtomPub page in the Codex which recommends testing with Tim Bray’s APE. Time to first error: 5 seconds.

A typo in the codex said the end point was http://[Blog URL]/wp-app.php service. Er, that’s probably not a space, huh? I do the right thing and fix it.

The right URL still gave either a 404 or the extremely helpful “No input file specified” (I forget which condition produced which). Googling for “No input file specified” brought up this helpful number. I had already checked that Dreamhost was running PHP as CGI, so the error means that something is not getting forwarded right.

Well it looks like Wordpress Atompub might not work with PHP4, so I switched Dreamhost to PHP5:

Dreamhost: how to change PHP version

Now I can surf to the URL and, thanks to the magic of the “cookies give you permission” hack inside wp-app.php and

<?xml version="1.0" encoding="utf-8" ?>
<service xmlns="http://www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom">
  <workspace>
    <atom:title>WordPress Workspace</atom:title>
    <collection href="http://ndanger.org/blog/wp-app.php/posts">
      <atom:title>WordPress Posts</atom:title>
      <accept>application/atom+xml;type=entry</accept>
      <categories href="http://ndanger.org/blog/wp-app.php/categories" />
    </collection>
    <collection href="http://ndanger.org/blog/wp-app.php/attachments">
      <atom:title>WordPress Media</atom:title>
      <accept>image/*</accept>
      <accept>audio/*</accept>
      <accept>video/*</accept>
    </collection>
  </workspace>
</service>

Hooray!

Mere steps away

Predictably, APE gives me an authorization error

APE credentials required screen shot

That’s covered in the codex. Unfortunately, neither the “HTTP Authentication with PHP” suggestion the codex links to or Joseph Scott’s get hack works.

Time to turn on logging in wp-app.php, and tail -f wp-app.log.

Maybe the rewrite rule isn’t getting applied? A test–by forwarding to my about page–works, so the rule is getting applied. Maybe the environment hacking isn’t working, so I redirect to a test script:

<?
$agent       = $_SERVER['HTTP_USER_AGENT'];
$ip          = $_SERVER['REMOTE_ADDR'];
$port        = $_SERVER['REMOTE_PORT'];
$d           = date ('dS \of F Y h:1:s A');
$ha          = $_SERVER['HTTP_AUTHORIZATION'];

echo "Your IP : $ip<br>";
echo "You are using : $agent<br>";
echo "You are connected through port : $port<br>";
echo "Today is : $d<br>";
echo "HTTP_AUTHORIZATION : $ha<br>";
echo "<br>_SERVER:<br>";
foreach ($_SERVER as $key => $value) {
  echo "$key: $value<br>";
}
echo "<br>_ENV:<br>";
foreach ($_ENV as $key => $value) {
  echo "$key: $value<br>";
}
echo "<br>_GET:<br>";
foreach ($_GET as $key => $value) {
  echo "$key: $value<br>";
}
?>

Both hacks seem to work in theory, except that the header I need–HTTP_AUTHORIZATION–seems to be absent, even if I use curl to manually set the headers using the example in the RFC.

More googling turns up the blocking of BasicAuth in WinXP SP2. Enable that. Nothing.

By now it’s 4 am and I’m still starting at a 404, so I post to the Dreamhost support forums.

And you got an answer, right?

Nah. That was all 3 days ago, so now I submitted a help ticket to the support staff. Have’t heard anything back yet, but that’s probably because I marked it as “Hey, it’d be nice, but it’s not holding me up”.

Anyone got any suggestions? I was going to set up a test directory and see if Apache’s .htaccess authentication–in BasicAuth mode–works.

In the meantime I’ll try out XML-RPC… it looks like there’s even an Emacs package.


UPDATE:

Heard back from dreamhost:

Hello,

Unfortunately, I’m not sure what would cause this, however, we can’t
really provide support for custom scripting, or custom mod_rewrite rules.
Sorry about that.

If you need anything else, please let us know.

Thanks!
Brian


A brief higher order function example in python

2007-12-24 / 13:19 / dave

The cast

dataset
An object for handling table-like data
dataset2
Version 2
dsutils
Utility functions for dataset
dsutils2
utility functions for dataset2

Act I

# dsutils2.py
from dataset2 import Dataset2
import dsutils as dsutils1
row2col2 = lambda ds2, *a, **ka: \
     Dataset2.from_dataset1(dsutils1.row2col(ds2.as_dataset1(), *a, **ka))
col2row2 = lambda ds2, *a, **ka: \
     Dataset2.from_dataset1(dsutils1.col2row(ds2.as_dataset1(), *a, **ka))

Act II

# dsutils2.py
# same
wrap_ds1f = lambda f: lambda ds2, *a, **ka: \
    Dataset2.from_dataset1(f(ds2.as_dataset1(), *a, **ka))
row2col2 = wrap_ds1f(dsutils1.row2col)
col2row2 = wrap_ds1f(dsutils1.col2row)
# in interpreter
>>> import(dsutils2)
>>> help(dsutils2)
Help on module dsutils2:
...
    col2row2 lambda ds2, *a, **ka
    ...
    row2col2 lambda ds2, *a, **ka
    ...
    wrap_ds1f lambda f
...
# That's not very helpful

Act III

def wrap_ds1func(ds1_func):
    """Create a wrapper for a function that accepts a dataset1 as it's first
    argument.  This creates a wrapper that converts a dataset2 into a
    dataset1, runs the function and converts the result  back to a dataset2.
    """
    def ds2_func(ds2, *args, **kargs):
        return Dataset.from_dataset1(
            ds1_func(ds2.to_dataset1(), *args, **kargs))
    ds2_func.__name__ = "%s2" % ds1_func.__name__
    ds2_func.__doc__ = "dataset2 version of %s, orig docs below.\n%s" \
                       % (ds1_func.__name__, ds1_func.__doc__)
    return ds2_func

row2col2 = wrap_ds1func(dsutils1.row2col)
col2row2 = wrap_ds1func(dsutils1.col2row)
# in interpreter
# same
Help on module dsutils2:
...
    col2row2 = col2row2(ds, *args, **kargs)
        dataset2 version of col2row, orig docs below.
        Convert a row with several columns into several rows with...
    row2col2 = row2col2(ds, *args, **kargs)
        dataset2 version of row2col, orig docs below.
        Convert several rows with related values into a single row with...
    wrap_ds1func(ds1_func)
        Create a wrapper for a function that accepts a dataset1 as it's first...
...
# better!

Epilogue

Longer, but reusable. And with readable help. Is it worth it? We’ll see. Either way higher order functions sure are nice.


By the year 2100, humans will not pass the ServoTron8000 test

2007-12-21 / 13:55 / dave

It is also important to note that once a computer does achieve a human level of intelligence, it will necessarily soar past it.
– Ray Kurzweil

Ray Kurzweil and Mitch Kapor debate the distant future of the Turing Test. Kurzweil–who I agree with more than Kapor–bases his argument on exponentials. Once self-aware machines exist they will improve rapidly for two reasons: 1) short iterations and 2) directed evolution.

If we assume that a computer that is self aware will take 1 micro-second (10^-6) to reprogram itself, one human generation (say 20 years) is more than 6 x 10^14 machine generations. Put another way, humans have been around for 2 million (2 x 10^6) years, or 10,000 generations. Machines would go through 10,000 generations in less than a second.

And for humans that was 10,000 generations of trial and error! Machines can direct their evolution. It’s true we can direct our technology, but imagine being able to, say, reprogram yourself to be 10 times smarter (or 10 times more beautiful which is what we’d probably all choose instead).

Combine the two and as soon as we develop machine intelligence bam! singularity. The very definition of the singularity is that we can’t predict beyond it.

To hell with that.

The lifeforms of the future–a more apt term than artificial intelligence–will be as different from us as we are to mice. Or perhaps as different from us as we are to current AI’s.

Maybe in 2078 some pundits will be sitting around arguing if humans can pass the ServoTron8000 Test. “No, ” one will argue “although the humans have a demonstrable grasp of intuition, they have never shown anything like full fledged holo-logical thought processes.”

I, for one, welcome our self-reprogramming digital overlords.


But say you did want to automatically enable all ssh keys on login…

2007-12-19 / 15:33 / dave

I launch my ssh enabled shell on demand, but if you are comfortable leaving it open (or using ssh-add -x to manually lock your ssh keys) add this to your startup file (.bash_profile or whatever):

eval `ssh-agent -s`; ssh-add

You’ll need to enter your password when you start the shell, but afterwards you’ve got all the password-less ssh goodness you can stand.


Debt and ugly code

2007-12-19 / 14:54 / dave

Jeff Atwood says that nobody cares what your code looks like, which–despite Jeff’s absolutely hilarious Joel Spolsky diss–is something Joel also talked about.

Ok, your customers don’t care what your code looks like… but they probably do care what it does. So saying “code beauty doesn’t matter” is the same as saying “ugly code works just as well as beautiful code”. You could argue that if the tests pass it’s all the same. But most customers also care about how well your code will function in the future. Does anyone really think that ugly code is as easy to change?

The agilists would say that ugly code is Technical Debt (well actually they would say that smelly code is technical debt but that’s irrelevant). Like real debt, technical debt accrues interest. Bug fixes muck up its guts and new features build on its interface, and, if your code is hideous, it’s a fair bet the interfaces aren’t much prettier.

But if we torture the metaphor some more (though I don’t consider this #2), debt can be a valid growth plan, just ask 90% of startups. Debt (technically selling your future worth for venture capital) is betting that you can run fast enough to reach profitability.

For code it’s hard to imagine you can build a critical mass of ugly code that will suddenly become beautiful, in fact that probably violates the second law of thermodynamics. In companies where speed & features are tied to lifespan (via convincing people to give you money), however, a fortune might let you amass a big enough army that you can reach that asymptote. Some people seem to be using the strategy, though it’s questionable how well that’s been working.

What of open source? For a commercial company where open source is largely a “look, you can see our code, so no lock-in!” feature, like Jeff’s example of his friend at a “large open source database company”, technical debt functions almost the same as in a closed-source company. I’d argue that it’s a bit worse; some people still eat fast food after having worked in the kitchen, but most swear it off and tell their friends that it’s full of tubes of grade E meat and cockroaches. It’s especially bad if your customers are programmers.

And if you want people to contribute to your open source project…

Despite what some people who inherited my code might think, I’ve always been a fan of beautiful code but I’ve come to understand the economics of “screw it”. But I think that the two can be rectified.

A small example to conclude. When I was in school I interned working on a speech recognized written in C (it was for an embedded device). Every new feature or bug-fix I worked on had an accompanying local code-cleanup. Ultimately I did a massive clean-up on the audio front-end, touching something like 75% of the file, and remember that this was a C file (i.e. it was big). The code review spanned two day but afterwards the code was smaller and, to everyone’s agreement, cleaner. I don’t have any metrics about it reducing bugs by 20% or anything, but I’m fairly confident it was worth the cost: about 1 full-time developer day and about 1 part-time intern week.


Emacs & version control

2007-12-18 / 23:08 / dave

Emacs does version control! If you’re tunneling your version control through SSH, you should probably set up those ssh keys


Ron Paul’s army & Reddit just jumped the shark (oh wait, maybe not)

2007-12-18 / 22:46 / dave

Since Reddit has become the official site of Ron Paul stories, I wasn’t surprised to see this video of Ron Paul denying evolution. Nor was I surprised by Dr. Paul’s answer as I’ve heard him give the Republican “It’s the volcanoes and cow farts!” answer to global warming (citation needed). I was surprised (well, only a little) by the initial responses on the Reddit comment thread. I don’t feel like reading through the comments to find some examples, but a lot of them were making excuses for Paul, saying that he didn’t mean it.

It was maddening… maddening enough that I was going to write a sternly worded blog post about it!

But then I went back later and read the new comments and whew what an improvement. In fact, they had gotten so insightful it restored some of wavering faith in Reddit.

Reddit’s “hot” page has recently been a train wreck. It’s all pictures and stories about Dr. Paul. Ok, occasionally I up-mod both, but I really don’t feel like I need to see 8 stories about the same one day fundraiser record. But normally reddit’s comments are sane enough that I figure there must be smart people using it. So I switched from watching the hot feed to watching the new items. Come on reddit, don’t let me down!


Scaling up / scaling down (or should I write that CSS templating thing or just learn Lisp)

2007-12-18 / 22:32 / dave

Part of the ongoing Django vs. Rails debate revolves around the templating language (link needed). The Django templates are intentionally limited. The developers claim this is a feature: it forces your templates to delegate the logic to your domain layer, simplifying your template language and making them easier for your poor beleagured designers to learn.

“Sure,” I says to myself “but not having to learn ANY new language would be better.” Since designers already design their content for CSS styling, why not search the HTML for ID’s and perform substitutions in code? The input would be plain HTML, so the designers–and tools for that matter–could understand it.

Great!

Er, well, assuming you actually have designers to worry about.

Scaling down

I was reading through A Common Lisp web startup test-case, two years after the Reddit switch and saw this:

The entire codebase of stix.to is written in Common Lisp. That is to say, html output is written in s-expressions and all our Javascript is written in Parenscript (if we choose to ignore a few quick hacks).

This seems to be the standard Lisp “write everything in sexps and compile it!” approach. Or as Paul Graham responded to a question about spacer .gifs: “…we’re smart enough to realize that html is object code”

So if you’ve got a small team, why worry about the designer at all? Let the coders use the language they know best… you know the one they use for programming. If the tools are good, the teams can be small; if it’s a 100 year language maybe your team can then shrink to the smallest possible size: one.

So you’re learning lisp?

Not yet. Scaling down to a small tight team sounds good, but really it’s just me dicking around with my web page. I already know HTML and Python; the most Lisp I’ve written is in my ~/.xemacs directory. Still, the more I hear about higher-higher-level languages (Haskell and Lisp, specifically) the more I start to think that I might be working in blub. More correctly, a collection of several blubs.

Anyway, I still think using HTML itself as a templating language makes a lot of sense. I’ll probably eventually get around to trying to write my dinky CSS templating engine beacause it’s a good kata and I think the idea is pretty cool. So if you are working with an army of designers… well, watch this space!