<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
 
  <title>Daniel's blog</title>
  <subtitle>Says it all</subtitle>
  <link href="http://storytotell.org/blog/" rel="self" />
  <link href="http://storytotell.org/" />
  <updated>2009-09-11T01:51:15-06:00</updated>
  <author>
    <name>Daniel Lyons</name>
    <email>fusion@storytotell.org</email>
  </author>
  <id>http://storytotell.org/</id>
  
  <entry>
    <title>Tablify CSV Utility</title>
    <link href="/blog/2009/09/11/tablify-csv-utility.html" />
    <id>tag:storytotell.org,2009-09-11:1252650400</id>
    <updated>2009-09-11T00:26:40-06:00</updated>
    <content type="html">&lt;script language=&quot;javascript&quot; src=&quot;http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js&quot;&gt;&lt;/script&gt;


&lt;p&gt;&lt;a href=&quot;/code/tablify.html&quot;&gt;Tablify&lt;/a&gt; is a tool to render CSV into tables of various formats, including HTML, tbl, and character art (both ASCII and Unicode). What motivated me to write this was discovering the way sexy cool UTF8 box characters. They appear to render really well in Apple&amp;rsquo;s new Menlo font—at least substantially better than they do in Microsoft&amp;rsquo;s Consolas font which was my previous fave. They seem to have a lot in common.&lt;/p&gt;

&lt;p&gt;Obligatory screenshot:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/code/tablify/screenshot.png&quot; alt=&quot;screenshot of tablify making pretty Unicode and ASCII art&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It also does HTML and &lt;a href=&quot;http://plan9.bell-labs.com/magic/man2html/1/tbl&quot;&gt;tbl(1)&lt;/a&gt; output.&lt;/p&gt;

&lt;p&gt;For kicks, I decided to write it in Haskell. I was actually contemplating what language I am the most fluent in, and I found myself running a lot of &lt;a href=&quot;http://www.dwheeler.com/sloccount/&quot;&gt;sloccount&lt;/a&gt; and wondering how exactly to compare my output in various languages. Two ways that seemed promising were to compare by number of files I've created and by number of lines of code. Here&amp;rsquo;s what I found:&lt;/p&gt;

&lt;table style=&quot;float: left; margin: 1em&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Language&lt;/th&gt;
      &lt;th&gt;# of Files&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;PHP&lt;/td&gt;
      &lt;td&gt;196&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Python&lt;/td&gt;
      &lt;td&gt;158&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Ruby&lt;/td&gt;
      &lt;td&gt;146&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Haskell&lt;/td&gt;
      &lt;td&gt;144&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;OCaml&lt;/td&gt;
      &lt;td&gt;88&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Prolog&lt;/td&gt;
      &lt;td&gt;75&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Lisp&lt;/td&gt;
      &lt;td&gt;63&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;C&lt;/td&gt;
      &lt;td&gt;44&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Erlang&lt;/td&gt;
      &lt;td&gt;41&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Clojure&lt;/td&gt;
      &lt;td&gt;30&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;


&lt;table style=&quot;float: left; margin: 1em&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Language&lt;/th&gt;
      &lt;th&gt;# of Lines&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;PHP&lt;/td&gt;
      &lt;td&gt;12,749&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Python&lt;/td&gt;
      &lt;td&gt;10,476&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Ruby&lt;/td&gt;
      &lt;td&gt;7,051&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;OCaml&lt;/td&gt;
      &lt;td&gt;5,817&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Lisp&lt;/td&gt;
      &lt;td&gt;5,058&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Haskell&lt;/td&gt;
      &lt;td&gt;3,256&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;C&lt;/td&gt;
      &lt;td&gt;1,412&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Prolog&lt;/td&gt;
      &lt;td&gt;1,349&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Clojure&lt;/td&gt;
      &lt;td&gt;1,177&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Erlang&lt;/td&gt;
      &lt;td&gt;964&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;


&lt;div style=&quot;clear: both&quot;&gt;&lt;/div&gt;


&lt;p&gt;I think there are some interesting natural divisions in here. For example, PHP, Python and Ruby are at the top in both categories. This reflects the fact that most of my professional programming experience has been in these languages. Towards the bottom, there&amp;rsquo;s my old friends C, Erlang and Clojure. Clojure undoubtedly is there because it&amp;rsquo;s the language I'm the newest with; the others because I don&amp;rsquo;t really like them that much (no offense, Erlang, I think you&amp;rsquo;re great).&lt;/p&gt;

&lt;p&gt;Funny thing about Prolog is, I think I start a lot of programs in Prolog but I don&amp;rsquo;t finish very many of them. Lisp has the opposite phenomena; it looks like my average Lisp file is fairly large, or else I have some fairly large Lisp files. I think I've only ever really written one small-to-medium sized app in Lisp and it certainly took more code than I expected it to.&lt;/p&gt;

&lt;p&gt;I think what&amp;rsquo;s interesting about this table is the stuff in the middle: OCaml and Haskell. I haven&amp;rsquo;t really written an OCaml program in some time but I still think of it as comparable in power and utility to C++, whereas Haskell is more on par in terms of complexity (thankfully neither is syntactically). So I started looking through my little files and I found a surprising spread of functionality amongst my Haskell programs. I had a few network servers, several parallel calculations, and some other stuff. Another interesting thing is that I didn&amp;rsquo;t really see any Haskell programs sitting in a broken incomplete state. There were several incomplete programs but few broken ones. I even found a program called &amp;ldquo;ritual.hs&amp;rdquo; which I had apparently started but abandoned for doing nothing but exactly this kind of linguistic archeology for me.&lt;/p&gt;

&lt;p&gt;As I was making my little chart I happened on the Unicode box drawing characters and the idea was born. So I decided to try doing it in Haskell and see if my attitude towards the world&amp;rsquo;s most useless programming language has changed.&lt;/p&gt;

&lt;p&gt;As usual, Haskell made some of the program trivial and other parts rather difficult. I didn&amp;rsquo;t find the I/O to be particularly hard. Most of the hair pulling came from changing interfaces and learning more about the module system, compounded by some issues having to do with the update to Mac OS X 10.6.&lt;/p&gt;

&lt;p&gt;Initially, I thought I would want my internal data structure to be an array, so I used Data.Array. I've since changed it to a list of lists, which would have been the obvious choice, and removed quite a bit of code in the process without (to my knowledge) affecting the runtime characteristics much.&lt;/p&gt;

&lt;p&gt;There was one rather nasty stack-blowing bug. I found a fair amount of advice online for fixing it but on reflection all I had to do was replace &lt;code&gt;foldr&lt;/code&gt; with &lt;code&gt;foldl'&lt;/code&gt; in one place. This took an hour or two.&lt;/p&gt;

&lt;p&gt;There are many places I feel the code is obtuse or poorly factored. Reading &lt;a href=&quot;http://book.realworldhaskell.org/read&quot;&gt;&lt;em&gt;Real World Haskell&lt;/em&gt;&lt;/a&gt; gives me the impression that I'm still an early grade of Haskell programmer. The book does a good job of showing the evolution of code from horrifying to mediocre to grand. Mine&amp;rsquo;s on the horrifying side in places, on the grand side in others, but not very far from the middle anywhere. I notice in addition to isolating the monadic code, I tend to try and limit the amount of it. There were several places, particularly in the HTML generation facility, where I thought I could eschew some complexity with the appropriate monad if only I knew which one and how to go about it.&lt;/p&gt;

&lt;p&gt;There&amp;rsquo;s also a built-in HTML combinator library I could be using instead of the homebrew one I made, as well as a third-party library that provides a &lt;code&gt;ByteString&lt;/code&gt;-based CSV parser. I tried to port over to it but ran into issues with &lt;code&gt;ByteString&lt;/code&gt;s and &lt;code&gt;String&lt;/code&gt; literals, then found the &lt;code&gt;Data.ByteString.Char8&lt;/code&gt; module and the third-party &lt;code&gt;Data.ByteString.UTF8&lt;/code&gt; module, but none of these seem to be using the same &lt;code&gt;ByteString&lt;/code&gt;. For example, if I import &lt;code&gt;Data.ByteString.UTF8&lt;/code&gt; and use its &lt;code&gt;fromString&lt;/code&gt; function, I can&amp;rsquo;t seem to pass it to the output function &lt;code&gt;Data.ByteString.putStrLn&lt;/code&gt;. This is currently one of the other big issues I have.&lt;/p&gt;

&lt;p&gt;So essentially, while I have understood monads for some time at least conceptually, I seem to lack both the library knowledge of which ones to use when, and the experiential knowledge of how to apply them and perhaps create my own. Obnoxiously, I see a fair number of little examples of monads in the programs I wrote to learn Haskell but I don&amp;rsquo;t see how to apply them inside an actual program effectively.&lt;/p&gt;

&lt;p&gt;I guess you could say my relationship with Haskell continues to be characterized by love of the ideals and hate of the hardships in getting to proficiency. I suspect a big part of the problem is trying to learn Haskell on one&amp;rsquo;s own. Yet, even if I write poor Haskell, I can be assured that if it compiles, it&amp;rsquo;s likely to be correct.&lt;/p&gt;

&lt;p&gt;Also next on my list of things to learn for Haskell is QuickCheck. My eyes glazed over when I read about coarbitrary: &amp;ldquo;The coarbitrary method interprets a value of type a as a generator transformer. It should be defined so that different values are interpreted as independent generator transformers.&amp;rdquo; Sure. I&amp;rsquo;ll get right on that.&lt;/p&gt;

&lt;p&gt;Highlights of my code:&lt;/p&gt;

&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;mac_classic&quot;&gt;
&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;--&lt;/span&gt; from FixedWidth.hs&lt;/span&gt;
&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;--&lt;/span&gt; given a table, return a list of the maximum widths of each column&lt;/span&gt;
&lt;span class=&quot;FunctionName&quot;&gt;columnWidths&lt;/span&gt; :: &lt;span class=&quot;UserDefinedConstant&quot;&gt;Table&lt;/span&gt; -&amp;gt; [&lt;span class=&quot;UserDefinedConstant&quot;&gt;Integer&lt;/span&gt;]
columnWidths table = &lt;span class=&quot;FunctionName&quot;&gt;map&lt;/span&gt; (&lt;span class=&quot;FunctionName&quot;&gt;maximum&lt;/span&gt; . &lt;span class=&quot;FunctionName&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;FunctionName&quot;&gt;length&lt;/span&gt;) $ transpose table

&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;--&lt;/span&gt; from HTML.hs&lt;/span&gt;
&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;--&lt;/span&gt; escapes the bare minimum of characters for safe embedding in HTML&lt;/span&gt;
&lt;span class=&quot;FunctionName&quot;&gt;htmlEscape&lt;/span&gt; :: &lt;span class=&quot;UserDefinedConstant&quot;&gt;String&lt;/span&gt; -&amp;gt; &lt;span class=&quot;UserDefinedConstant&quot;&gt;String&lt;/span&gt;
htmlEscape html = &lt;span class=&quot;FunctionName&quot;&gt;foldl&lt;/span&gt;' process html mappings
    &lt;span class=&quot;Keyword&quot;&gt;where&lt;/span&gt;
        mappings = [(&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&amp;amp;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&amp;amp;amp;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;), (&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&amp;lt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&amp;amp;lt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;), (&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&amp;gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&amp;amp;gt;&lt;span class=&quot;String&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;)]
        process text (find, replace) = subRegex (mkRegex find) text replace
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Pretty much everything else is a mess. I welcome any advice.&lt;/p&gt;

&lt;script language=&quot;javascript&quot;&gt;
$(function() {
    $('table tbody tr:odd').addClass('odd');
    $('table tbody td:odd').addClass('numeric');
});
&lt;/script&gt;

</content>
  </entry>
  
  <entry>
    <title>In Defense of Unreason</title>
    <link href="/blog/2009/08/21/in-defense-of-unreason.html" />
    <id>tag:storytotell.org,2009-08-21:1250838814</id>
    <updated>2009-08-21T01:13:34-06:00</updated>
    <content type="html">&lt;p&gt;This is my analysis of &lt;a href=&quot;http://www.independent.co.uk/opinion/commentators/johann-hari/johann-hari-republicans-religion-and-the-triumph-of-unreason-1773994.html&quot;&gt;Republicans, religion and the triumph of unreason&lt;/a&gt; by Johann Hari.&lt;/p&gt;

&lt;p&gt;First of all, in broad strokes, I agree with the description of the symptoms in the essay. The American right is basically living in another universe. When Bush was in power, the liberal conspiracy theories were plausible: that Bush wanted to invade Iran, for example. The new conspiracy theories are just insane. I am reasonably certain that Obama&amp;rsquo;s first health care reform bill will be a major disaster, but only because they are rushing to get it through and with a bunch of democrats in power it&amp;rsquo;s like to be padded with tons of pork. The fears that are being passed around are completely absurd. Isn&amp;rsquo;t it enough to be afraid of the likeliest case, that we&amp;rsquo;ll just be saddled with more useless government? I hope Obama can do better, but statistically his odds are not great.&lt;/p&gt;

&lt;p&gt;However, I think it&amp;rsquo;s silly to blame religion for this problem. With &lt;a href=&quot;http://en.wikipedia.org/wiki/Demographics_of_atheism#North_America&quot;&gt;atheism at around 15% of the population&lt;/a&gt;, Obama is not in power because of some kind of nationwide repudiation of religion. This is the chief vanity of the left, that it represents a harbinger of the kind of &amp;ldquo;rationality&amp;rdquo; espoused by certain intellectuals who happen to be atheists.&lt;/p&gt;

&lt;p&gt;It&amp;rsquo;s tempting to put the blame at religion&amp;rsquo;s doorstep because it was supposedly the rallying cry of the right. But the truth about the American right is that it is a massive hodgepodge of vastly different groups with almost nothing in common. And guess what? So is the left. Politicians use propaganda to acquire power, not because they actually believe it. How can you accuse Bush and his cronies of corruption and then simultaneously blame their ideals? Obviously if their ideals meant anything they wouldn&amp;rsquo;t have been corrupt in the first place. Instead we hate them for what they've done and then we hate them more for being hypocrites. This is a game two can (and will) play.&lt;/p&gt;

&lt;p&gt;It&amp;rsquo;s also tempting to blame religion because it is a handy target and has few defenders. Especially when couched in gerrymandered terminology like &amp;ldquo;organized religion.&amp;rdquo; What makes this article&amp;rsquo;s kind of analysis so disgusting to me is the assumption that the essence of religion is faith. In American Christianity, the foundation of the religion is faith, but this is like saying the problem with Congress is that they meet in a building with a floor. The problem with the American right is that they&amp;rsquo;re credulous, but guess what? That&amp;rsquo;s the problem with the American left as well! The common factor I see is the American education system. &lt;a href=&quot;http://nces.ed.gov/pubs2006/homeschool/index.asp&quot;&gt;The 2.2% that are homeschooled&lt;/a&gt; would be just as statistically irrelevant to the counter-argument as the atheists above. If what you hate is Christianity, why not just come right out and say it and why?&lt;/p&gt;

&lt;p&gt;For every Bible-thumping redneck we love to hate, there&amp;rsquo;s a suburban driving a gas guzzling SUV with a bike rack on top who lives 20 miles from where they work. Evidence from psychological studies that abortions are highly detrimental to the mother&amp;rsquo;s mental health are discarded from the abortion debate because that debate is about &amp;ldquo;rights,&amp;rdquo; but they then turn around and spin arguments about the environment&amp;rsquo;s health being grounds for restricting rights. Which one is it? You can argue both rationally. It would be better economically to legalize weed and prostitution and regulate them. Which rational argument do you want to make?&lt;/p&gt;

&lt;p&gt;This infatuation with rationality is handy in a debate, but what do you do if you have contradictory facts? Upon what do you base a rational debate about ethics? Assertions? Religions supply a foundation for these kinds of debates that cannot otherwise be deduced. There is no fundamental particle that carries good or evil. This means science is of limited use in many of the most important debates. You'd think the author of this essay encourages people to marry on the basis of empirical evidence from observation and calculations! Apparently mechanical hearts can also bleed!&lt;/p&gt;

&lt;p&gt;The best part is, this author doesn&amp;rsquo;t have a single statistic to back up his assertions about religion. It&amp;rsquo;s all speculation. Angels dancing on the head of a pin, if you will. But because it is so well argued, it will further reinforce in the mind of the intellectual left that they&amp;rsquo;re fighting a war against religion rather than a war against propaganda and irrationality. In other words, this article is itself leftist propaganda that distracts from the real issues with made-up facts! Isn&amp;rsquo;t that the very thing this article is against? This self-certainty about ones interpretation of reality is groupthink exactly and it is what causes the continual pendulum sway of American politics.&lt;/p&gt;

&lt;p&gt;Groupthink is certainly preventing the health care debate from taking shape the right way. I think intentional meddling might be a bigger problem. Responding to redder groupthink with bluer groupthink isn&amp;rsquo;t a solution. Mr. Hari would do better to analyze both sides of the debate with the same kind of cynicism.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>PostgreSQL 8.4: Windowing Functions</title>
    <link href="/blog/2009/08/12/postgresql-84-windowing-functions.html" />
    <id>tag:storytotell.org,2009-08-12:1250140683</id>
    <updated>2009-08-12T23:18:03-06:00</updated>
    <content type="html">&lt;p&gt;PostgreSQL 8.4 is the first version to introduce OLAP functionality in the form of the windowing functions. These functions are new to me too but they look quite interesting and powerful. They will greatly simplify combining many large SQL statements into simpler ones for complex reporting, as well as make it possible to push some &amp;ldquo;fixup&amp;rdquo; processing that depends on ordering back into the database.&lt;/p&gt;

&lt;p&gt;As an example, I'm going to be using the &lt;code&gt;orders&lt;/code&gt; table from the (Dell store example database)[http://pgfoundry.org/projects/dbsamples/]. A few rows from this table look like this:&lt;/p&gt;

&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;mac_classic&quot;&gt;
&lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt; 
  orderid, orderdate, customerid, 
  netamount::&lt;span class=&quot;Keyword&quot;&gt;text&lt;/span&gt;::&lt;span class=&quot;Keyword&quot;&gt;money&lt;/span&gt;, 
  tax::&lt;span class=&quot;Keyword&quot;&gt;text&lt;/span&gt;::&lt;span class=&quot;Keyword&quot;&gt;money&lt;/span&gt;, 
  totalamount::&lt;span class=&quot;Keyword&quot;&gt;text&lt;/span&gt;::&lt;span class=&quot;Keyword&quot;&gt;money&lt;/span&gt; 
&lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; orders &lt;span class=&quot;Keyword&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;Number&quot;&gt;10&lt;/span&gt;;
&lt;/pre&gt;
&lt;/div&gt;


&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;orderid&lt;/th&gt;
      &lt;th&gt;orderdate&lt;/th&gt;
      &lt;th&gt;customerid&lt;/th&gt;
      &lt;th&gt;netamount&lt;/th&gt;
      &lt;th&gt;tax&lt;/th&gt;
      &lt;th&gt;totalamount&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;1&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004-01-27&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;7888&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$313.24&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$25.84&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$339.08&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;2&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004-01-01&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;4858&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$54.90&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$4.53&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$59.43&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;3&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004-01-17&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;15399&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$160.10&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$13.21&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$173.31&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;4&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004-01-28&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;17019&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$106.67&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$8.80&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$115.47&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;5&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004-01-09&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;14771&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$256.00&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$21.12&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$277.12&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;6&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004-01-11&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;13734&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$382.59&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$31.56&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$414.15&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;7&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004-01-05&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;17622&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$256.44&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$21.16&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$277.60&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;8&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004-01-18&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;8331&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$67.85&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$5.60&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$73.45&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;9&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004-01-06&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;14902&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$29.82&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$2.46&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$32.28&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;10&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004-01-18&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;15112&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$20.78&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$1.71&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$22.49&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
&lt;/table&gt;


&lt;p&gt;Without getting into the new features, it&amp;rsquo;s possible to generate interesting summary information. You might want to see how much you made each month, for example:&lt;/p&gt;

&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;mac_classic&quot;&gt;
&lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt;
  EXTRACT(MONTH &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; orderdate) AS month, 
  EXTRACT(YEAR &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; orderdate) AS year, 
  SUM(totalamount)::&lt;span class=&quot;Keyword&quot;&gt;text&lt;/span&gt;::&lt;span class=&quot;Keyword&quot;&gt;money&lt;/span&gt; AS total 
&lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; orders 
&lt;span class=&quot;Keyword&quot;&gt;GROUP BY&lt;/span&gt; year, month 
&lt;span class=&quot;Keyword&quot;&gt;ORDER BY&lt;/span&gt; year, month;
&lt;/pre&gt;
&lt;/div&gt;


&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;month&lt;/th&gt;
      &lt;th&gt;year&lt;/th&gt;
      &lt;th&gt;total&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;1&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$215,898.76&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;2&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$216,792.09&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;3&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$210,564.40&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;4&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$216,642.01&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;5&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$213,395.19&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;6&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$216,412.59&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;7&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$206,707.38&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;8&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$210,115.92&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;9&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$213,034.71&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;10&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$211,952.17&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;11&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$216,373.17&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;12&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$219,498.40&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;


&lt;p&gt;What we&amp;rsquo;re doing here is just grouping on some computed data, in this case, EXTRACT is giving us the information we need from the DATE to use it for grouping. An interesting fact about grouping is that it restricts you to relational calculations. You cannot, for example, do something different depending on where you are in the calculation. Grouping also applies to the whole statement, making it hard to make a complex report without using many subselects.&lt;/p&gt;

&lt;p&gt;The window functions were created to support OLAP or Off-Line Analytical Processing, as opposed to OLTP or On-Line Transaction Processing which is what SQL RDBMSes are generally thought of. Despite this nobody has managed to give me an example of what OLAP is like in reality, so it must be a pretty specific and rare topic. However, the nutshell of windowing is that it lets you combine multiple different groupings in the same statement, or do calculations with respect to your current location in the generated result relation.&lt;/p&gt;

&lt;p&gt;What if you want to also calculate the year-to-date earnings for each month? Without the window functions I'm not sure this is possible in straight SQL. What if you want to rank each month relative to the other months in terms of income?&lt;/p&gt;

&lt;p&gt;Windowing essentially is a modification of an aggregate function with a new clause, &lt;code&gt;OVER&lt;/code&gt;. There are some new functions too, like &lt;code&gt;rank()&lt;/code&gt; which is essentially the row number. The &lt;code&gt;OVER&lt;/code&gt; clause itself is built out of two optional expressions, an &lt;code&gt;ORDER BY&lt;/code&gt; expression which is the same as the kind you already know, and a &lt;code&gt;PARTITION BY&lt;/code&gt; which works much like &lt;code&gt;GROUP BY&lt;/code&gt; with some extra features.&lt;/p&gt;

&lt;p&gt;The rank of a given month is clearly related to the total amount made in that month, so its &lt;code&gt;OVER&lt;/code&gt; clause will be &lt;code&gt;ORDER BY total DESC&lt;/code&gt; since the largest amount should come first. Let&amp;rsquo;s add that to the query:&lt;/p&gt;

&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;mac_classic&quot;&gt;
&lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt; 
  EXTRACT(MONTH &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; orderdate) AS month,
  EXTRACT(YEAR &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; orderdate) AS year, 
  SUM(totalamount)::&lt;span class=&quot;Keyword&quot;&gt;text&lt;/span&gt;::&lt;span class=&quot;Keyword&quot;&gt;money&lt;/span&gt; AS total,
  rank() OVER (&lt;span class=&quot;Keyword&quot;&gt;ORDER BY&lt;/span&gt; total DESC)
&lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; orders
&lt;span class=&quot;Keyword&quot;&gt;GROUP BY&lt;/span&gt; year, month 
&lt;span class=&quot;Keyword&quot;&gt;ORDER BY&lt;/span&gt; year, month;
&lt;/pre&gt;
&lt;/div&gt;


&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;month&lt;/th&gt;
      &lt;th&gt;year&lt;/th&gt;
      &lt;th&gt;total&lt;/th&gt;
      &lt;th&gt;rank&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;1&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$215,898.76&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;6&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;2&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$216,792.09&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;3&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$210,564.40&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;10&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;4&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$216,642.01&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;5&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$213,395.19&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;6&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$216,412.59&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;7&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$206,707.38&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;12&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;8&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$210,115.92&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;11&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;9&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$213,034.71&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;8&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;10&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$211,952.17&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;9&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;11&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$216,373.17&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td class=&quot;numeric&quot;&gt;12&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;$219,498.40&lt;/td&gt;
      &lt;td class=&quot;numeric&quot;&gt;1&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;


&lt;p&gt;This is already giving us some information we ordinarily would have had to infer or calculate on the other side. Now let&amp;rsquo;s say you want to also see the earnings year-to-date for each month. Our first try isn&amp;rsquo;t going to work:&lt;/p&gt;

&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;mac_classic&quot;&gt;
&lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt; 
  EXTRACT(MONTH &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; orderdate) AS month,
  EXTRACT(YEAR &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; orderdate) AS year, 
  SUM(totalamount)::&lt;span class=&quot;Keyword&quot;&gt;text&lt;/span&gt;::&lt;span class=&quot;Keyword&quot;&gt;money&lt;/span&gt; AS total,
  rank() OVER (&lt;span class=&quot;Keyword&quot;&gt;ORDER BY&lt;/span&gt; total DESC),
  SUM(total)::&lt;span class=&quot;Keyword&quot;&gt;text&lt;/span&gt;::&lt;span class=&quot;Keyword&quot;&gt;money&lt;/span&gt; OVER (&lt;span class=&quot;Keyword&quot;&gt;ORDER BY&lt;/span&gt; year, month) AS ytd
&lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; orders
&lt;span class=&quot;Keyword&quot;&gt;GROUP BY&lt;/span&gt; year, month 
&lt;span class=&quot;Keyword&quot;&gt;ORDER BY&lt;/span&gt; year, month;
&lt;/pre&gt;
&lt;/div&gt;


&lt;pre&gt;&lt;code&gt;
ERROR:  column &quot;total&quot; does not exist
LINE 6:   SUM(total)::text::money OVER (ORDER BY year, month) AS ytd
              ^
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;What this message is showing us is that our &lt;tt&gt;SUM(totalamount) AS total&lt;/tt&gt; isn't visible inside the &lt;tt&gt;OVER&lt;/tt&gt; clause of the other column. Fortunately, this is easy to work around by making the original query a subselect of the new query like so:&lt;/p&gt;


&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;mac_classic&quot;&gt;
&lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt;
  *,
  SUM(total) OVER (&lt;span class=&quot;Keyword&quot;&gt;ORDER BY&lt;/span&gt; year, month)::&lt;span class=&quot;Keyword&quot;&gt;text&lt;/span&gt;::&lt;span class=&quot;Keyword&quot;&gt;money&lt;/span&gt; AS ytd
&lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt;
  (&lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt; 
     EXTRACT(MONTH &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; orderdate) AS month,
     EXTRACT(YEAR &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; orderdate) AS year, 
     SUM(totalamount)::&lt;span class=&quot;Keyword&quot;&gt;text&lt;/span&gt;::&lt;span class=&quot;Keyword&quot;&gt;money&lt;/span&gt; AS total,
     rank() OVER (&lt;span class=&quot;Keyword&quot;&gt;ORDER BY&lt;/span&gt; total DESC)
   &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; orders
   &lt;span class=&quot;Keyword&quot;&gt;GROUP BY&lt;/span&gt; year, month 
   &lt;span class=&quot;Keyword&quot;&gt;ORDER BY&lt;/span&gt; year, month) AS q
&lt;span class=&quot;Keyword&quot;&gt;ORDER BY&lt;/span&gt; year, month;
&lt;/pre&gt;
&lt;/div&gt;


&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;month&lt;/th&gt;
 &lt;th&gt;year&lt;/th&gt;
 &lt;th&gt;total&lt;/th&gt;
 &lt;th&gt;rank&lt;/th&gt;
 &lt;th&gt;ytd&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td class=&quot;numeric&quot;&gt;1&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;$215,898.76&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;6&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;$215,898.76&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td class=&quot;numeric&quot;&gt;2&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;$216,792.09&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;2&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;$432,690.85&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td class=&quot;numeric&quot;&gt;3&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;$210,564.40&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;10&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;$643,255.25&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td class=&quot;numeric&quot;&gt;4&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;$216,642.01&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;3&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;$859,897.26&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td class=&quot;numeric&quot;&gt;5&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;$213,395.19&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;7&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;$1,073,292.45&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td class=&quot;numeric&quot;&gt;6&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;$216,412.59&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;4&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;$1,289,705.04&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td class=&quot;numeric&quot;&gt;7&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;$206,707.38&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;12&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;$1,496,412.42&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td class=&quot;numeric&quot;&gt;8&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;$210,115.92&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;11&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;$1,706,528.34&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td class=&quot;numeric&quot;&gt;9&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;$213,034.71&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;8&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;$1,919,563.05&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td class=&quot;numeric&quot;&gt;10&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;$211,952.17&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;9&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;$2,131,515.22&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td class=&quot;numeric&quot;&gt;11&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;$216,373.17&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;5&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;$2,347,888.39&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td class=&quot;numeric&quot;&gt;12&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;2004&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;$219,498.40&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;1&lt;/td&gt;
 &lt;td class=&quot;numeric&quot;&gt;$2,567,386.79&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;


&lt;p&gt;As an aside, you really shouldn&amp;rsquo;t use the &lt;code&gt;money&lt;/code&gt; type in PostgreSQL. I only used it here to make the formatting a little prettier. In general you should use &lt;code&gt;NUMERIC(X,2)&lt;/code&gt; where X is a number larger than you expect to ever have in your database. If you have to deal with multiple countries (or might someday) you should also track the currency code and perhaps even the conversion rate to your native currency code on that particular day. It gets complex in a hurry.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Fibonacci in PostgreSQL</title>
    <link href="/blog/2009/08/12/fibonacci-in-postgresql.html" />
    <id>tag:storytotell.org,2009-08-12:1250092457</id>
    <updated>2009-08-12T09:54:17-06:00</updated>
    <content type="html">&lt;p&gt;I should have brought this up in the previous post. The question on everyone&amp;rsquo;s mind must be, if SQL has recursion, how do you compute the Fibonacci sequence? Here&amp;rsquo;s how:&lt;/p&gt;

&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;mac_classic&quot;&gt;
&lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt; fib &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt;
(WITH RECURSIVE fibonacci(fib, fib&lt;span class=&quot;Number&quot;&gt;1&lt;/span&gt;) AS (
  &lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;Number&quot;&gt;1&lt;/span&gt;, &lt;span class=&quot;Number&quot;&gt;0&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;UNION&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;Number&quot;&gt;1&lt;/span&gt; :: &lt;span class=&quot;Keyword&quot;&gt;NUMERIC&lt;/span&gt;, &lt;span class=&quot;Number&quot;&gt;1&lt;/span&gt; :: &lt;span class=&quot;Keyword&quot;&gt;NUMERIC&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;UNION&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt; fib + fib&lt;span class=&quot;Number&quot;&gt;1&lt;/span&gt;, fib &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; fibonacci
)
&lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt; * &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; fibonacci) AS fibs &lt;span class=&quot;Keyword&quot;&gt;LIMIT&lt;/span&gt; &lt;span class=&quot;Number&quot;&gt;1000&lt;/span&gt;;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;I couldn&amp;rsquo;t see a way to refer to the previous two rows, so instead I just carry along the previous row&amp;rsquo;s value inside the current row, so the next calculation can see the previous two values. I wrapped the whole thing in a &lt;code&gt;SELECT&lt;/code&gt; to hide &lt;code&gt;fib1&lt;/code&gt;. The &lt;code&gt;NUMERIC&lt;/code&gt; type gives you arbitrary-precision math (or at least to 1000 decimals). Without it, the maximum number you can calculate is 46 because 1134903170 + 1836311903 overflows the normal integer.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Postgresql 8.4 Recursive Queries</title>
    <link href="/blog/2009/08/11/postgresql84-recursive-queries.html" />
    <id>tag:storytotell.org,2009-08-11:1250011067</id>
    <updated>2009-08-11T11:17:47-06:00</updated>
    <content type="html">&lt;p&gt;Apparently I missed the news: PostgreSQL 8.4 came out on July 1st! This release brings two new features and greatly improves one old feature. This post will be about recursive queries using &lt;code&gt;WITH&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A classic problem in SQL is how to model trees. In web development you often have to store nested subcategories or model pages as files inside folders. The simplest proper way to model this relationship in SQL is to add a self-referencing column to the table to point to a row&amp;rsquo;s parent row. For example, you might represent the file path &lt;code&gt;/foo/bar/baz.html&lt;/code&gt; with the following table:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;&lt;tr&gt;&lt;th&gt;id&lt;/th&gt;&lt;th&gt;name&lt;/th&gt;&lt;th&gt;parent_id&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;/&lt;/td&gt;&lt;td&gt;&lt;i&gt;NULL&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;foo&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;bar&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;/tr&gt;
    &lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;baz.html&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;/tr&gt;
  &lt;/tbody&gt;
  &lt;tfoot&gt;&lt;tr&gt;&lt;th colspan=&quot;3&quot;&gt;&lt;code&gt;fs&lt;/code&gt; table&lt;/tr&gt;&lt;/tfoot&gt;
&lt;/table&gt;


&lt;p&gt;This structure has a number of pleasant features from a relational perspective. For one thing, you can rename a folder by modifying just one row. You can insert a new node anywhere in this hierarchy by just knowing the parent folder&amp;rsquo;s ID. You can also move a node anywhere in the hierarchy by modifying that one ID.&lt;/p&gt;

&lt;p&gt;Unfortunately, from an implementation perspective, this structure sucks for doing many common operations: computing a string version of a path, for example, or listing all the descendants under a given node. There are many simpler, less normalized methods that are better at these common operations at the expense of some relational normality. (One I have used is to have outside software maintain a text path in the database.)&lt;/p&gt;

&lt;p&gt;With recursive queries it&amp;rsquo;s now possible to recreate the string path, find all descendant nodes, and get all of the parent nodes up to the root. I'm going to show you how to do each of these things with just one query apiece.&lt;/p&gt;

&lt;p&gt;All of the magic comes down to the &lt;code&gt;WITH&lt;/code&gt; statement. This statement is provided both to support recursive queries and also for common table expressions or CTEs. If you find yourself repeating the same table definition in your query you can pull it out and put it above the statement in a &lt;code&gt;WITH&lt;/code&gt; clause. You can think of this as a way to make a temporary view just for a single SQL statement.&lt;/p&gt;

&lt;p&gt;The basic structure you&amp;rsquo;re looking at is this:&lt;/p&gt;

&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;mac_classic&quot;&gt;
WITH temp_view(col&lt;span class=&quot;Number&quot;&gt;1&lt;/span&gt;, ...) AS (&lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt; ...)
&lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt; ...;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;If you use &lt;code&gt;temp_view&lt;/code&gt; inside the &lt;code&gt;SELECT&lt;/code&gt; within the &lt;code&gt;WITH&lt;/code&gt; statement, you have to add &lt;code&gt;RECURSIVE&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;mac_classic&quot;&gt;
WITH RECURSIVE temp_view(col&lt;span class=&quot;Number&quot;&gt;1&lt;/span&gt;, ...) AS (&lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt; ...)
&lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt; ...;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;All three of the queries I'm going to show you rely on the recursive flavor in order to do their work. There is a design pattern in the inner &lt;code&gt;SELECT&lt;/code&gt; whenever a recursive query is written, which is related to the notion of recursion. You must define a &lt;em&gt;base case&lt;/em&gt; and an &lt;em&gt;inductive case&lt;/em&gt;. The base case sets up the initial conditions for the recursion, usually the first set of rows for the result. The inductive case uses the rows which have already been computed to compute more rows and decide whether or not the recursion is complete.&lt;/p&gt;

&lt;p&gt;For example, to find the breadcrumbs for a given path, the base case is the initial path. The inductive case is just to find the parent path. If there&amp;rsquo;s no parent path, the recursion is complete. Inside the SQL, you&amp;rsquo;ll do a naïve &lt;code&gt;SELECT&lt;/code&gt; and &lt;code&gt;UNION&lt;/code&gt; it with a &lt;code&gt;SELECT&lt;/code&gt; that uses the recursively defined name. You can read more about this in the (PostgreSQL Documentation)[http://www.postgresql.org/docs/8.4/interactive/queries-with.html]. So the SQL will look like this:&lt;/p&gt;

&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;mac_classic&quot;&gt;
WITH RECURSIVE breadcrumb(id, name, parent_id) AS (
  &lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt; id, name, parent_id &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; fs &lt;span class=&quot;Keyword&quot;&gt;WHERE&lt;/span&gt; id = &lt;span class=&quot;Number&quot;&gt;4&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;UNION&lt;/span&gt;
    &lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt; fs.id, fs.name, fs.parent_id &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; fs, breadcrumb
    &lt;span class=&quot;Keyword&quot;&gt;WHERE&lt;/span&gt; breadcrumb.parent_id = fs.id)
&lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt; * &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; breadcrumb;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The base case gets all of the information for the row we&amp;rsquo;re interested in, which is the row with ID 4. The inductive case takes the previously generate row and gets the row from &lt;code&gt;fs&lt;/code&gt; with the ID of the parent ID of the row with ID 4, which in this case is the row with ID 3. The next iteration finds the row from &lt;code&gt;fs&lt;/code&gt; with the ID of the parent ID of the row with ID 3, and so on.&lt;/p&gt;

&lt;p&gt;If you&amp;rsquo;re following along at home, run this SQL to set up our initial conditions:&lt;/p&gt;

&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;mac_classic&quot;&gt;
&lt;span class=&quot;Keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;FunctionName&quot;&gt;fs&lt;/span&gt; (
  id &lt;span class=&quot;Keyword&quot;&gt;SERIAL&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;PRIMARY KEY&lt;/span&gt;, 
  name &lt;span class=&quot;Keyword&quot;&gt;VARCHAR&lt;/span&gt;, 
  parent_id &lt;span class=&quot;Keyword&quot;&gt;INTEGER&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;REFERENCES&lt;/span&gt; fs(id)
);

&lt;span class=&quot;Keyword&quot;&gt;INSERT INTO&lt;/span&gt; fs (name, parent_id) 
&lt;span class=&quot;Keyword&quot;&gt;VALUES&lt;/span&gt; (&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;/'&lt;/span&gt;, &lt;span class=&quot;Keyword&quot;&gt;NULL&lt;/span&gt;), (&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;foo'&lt;/span&gt;, &lt;span class=&quot;Number&quot;&gt;1&lt;/span&gt;), (&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;bar'&lt;/span&gt;, &lt;span class=&quot;Number&quot;&gt;2&lt;/span&gt;), (&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;baz.html'&lt;/span&gt;, &lt;span class=&quot;Number&quot;&gt;3&lt;/span&gt;);
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Now you should get this output from the above SQL:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th align=&quot;center&quot;&gt;id&lt;/th&gt;
      &lt;th align=&quot;center&quot;&gt;name&lt;/th&gt;
      &lt;th align=&quot;center&quot;&gt;parent_id&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;baz.html&lt;/td&gt;
      &lt;td&gt;3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;bar&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;foo&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;/&lt;/td&gt;
      &lt;td&gt;&lt;i&gt;NULL&lt;/i&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;


&lt;p&gt;Let&amp;rsquo;s work on the other two problems: getting a string path and listing descendants of a given node. We&amp;rsquo;ll use the root node for the sake of fun and add a few extra nodes so that the tree is fuller and tests our query a little better:&lt;/p&gt;

&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;mac_classic&quot;&gt;
&lt;span class=&quot;Keyword&quot;&gt;INSERT INTO&lt;/span&gt; fs (name, parent_id) 
&lt;span class=&quot;Keyword&quot;&gt;VALUES&lt;/span&gt; (&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;under foo'&lt;/span&gt;, &lt;span class=&quot;Number&quot;&gt;2&lt;/span&gt;), (&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;under bar'&lt;/span&gt;, &lt;span class=&quot;Number&quot;&gt;3&lt;/span&gt;), (&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;under bar 2'&lt;/span&gt;, &lt;span class=&quot;Number&quot;&gt;3&lt;/span&gt;), 
       (&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;baz'&lt;/span&gt;, &lt;span class=&quot;Number&quot;&gt;2&lt;/span&gt;), (&lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;bazzle.html'&lt;/span&gt;, &lt;span class=&quot;Number&quot;&gt;8&lt;/span&gt;);
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Now, to do this query the base case would seem to be the root of the tree. In thinking about what kind of result we want, I think for convenience we might want the current node&amp;rsquo;s name and the full path of this node in addition to the regular columns. We&amp;rsquo;re going to assume that the root of the tree is the node with ID 1, which gives us this base case:&lt;/p&gt;

&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;mac_classic&quot;&gt;
WITH RECURSIVE &lt;span class=&quot;Keyword&quot;&gt;path&lt;/span&gt;(name, &lt;span class=&quot;Keyword&quot;&gt;path&lt;/span&gt;, parent, id, parent_id) AS (
  &lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt; name, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;/'&lt;/span&gt;, &lt;span class=&quot;Keyword&quot;&gt;NULL&lt;/span&gt;, id, parent_id &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; fs &lt;span class=&quot;Keyword&quot;&gt;WHERE&lt;/span&gt; id = &lt;span class=&quot;Number&quot;&gt;1&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Now the inductive case just takes that case and computes the descendant nodes of that node by looking at their parent_id:&lt;/p&gt;

&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;mac_classic&quot;&gt;
  &lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt;
    fs.name, 
    parentpath.&lt;span class=&quot;Keyword&quot;&gt;path&lt;/span&gt; || 
      CASE parentpath.&lt;span class=&quot;Keyword&quot;&gt;path&lt;/span&gt; 
        WHEN &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;/'&lt;/span&gt; THEN &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;'&lt;/span&gt; 
        ELSE &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;/'&lt;/span&gt; 
      END || fs.name,
    parentpath.&lt;span class=&quot;Keyword&quot;&gt;path&lt;/span&gt;, fs.id, fs.parent_id
  &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; fs, &lt;span class=&quot;Keyword&quot;&gt;path&lt;/span&gt; as parentpath
  &lt;span class=&quot;Keyword&quot;&gt;WHERE&lt;/span&gt; fs.parent_id = parentpath.id
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Calculating the path, the &lt;code&gt;CASE&lt;/code&gt; statement you see there is a hack to prevent extra &amp;lsquo;/&amp;rsquo;s at the start of the path because of the UNIX convention of paths starting at /. Otherwise you see I'm just taking the name of this node and concatenating the parent path with my name, separated by /&amp;rsquo;s. So this is the full version:&lt;/p&gt;

&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;mac_classic&quot;&gt;
WITH RECURSIVE &lt;span class=&quot;Keyword&quot;&gt;path&lt;/span&gt;(name, &lt;span class=&quot;Keyword&quot;&gt;path&lt;/span&gt;, parent, id, parent_id) AS (
  &lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt; name, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;/'&lt;/span&gt;, &lt;span class=&quot;Keyword&quot;&gt;NULL&lt;/span&gt;, id, parent_id &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; fs &lt;span class=&quot;Keyword&quot;&gt;WHERE&lt;/span&gt; id = &lt;span class=&quot;Number&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;UNION&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt;
    fs.name, 
    parentpath.&lt;span class=&quot;Keyword&quot;&gt;path&lt;/span&gt; || 
      CASE parentpath.&lt;span class=&quot;Keyword&quot;&gt;path&lt;/span&gt; 
        WHEN &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;/'&lt;/span&gt; THEN &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;'&lt;/span&gt; 
        ELSE &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;/'&lt;/span&gt; 
      END || fs.name,
    parentpath.&lt;span class=&quot;Keyword&quot;&gt;path&lt;/span&gt;, fs.id, fs.parent_id
  &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; fs, &lt;span class=&quot;Keyword&quot;&gt;path&lt;/span&gt; as parentpath
  &lt;span class=&quot;Keyword&quot;&gt;WHERE&lt;/span&gt; fs.parent_id = parentpath.id)
&lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt; * &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;path&lt;/span&gt;;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Let&amp;rsquo;s see what it does!&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;name&lt;/th&gt;
      &lt;th&gt;path&lt;/th&gt;
      &lt;th&gt;parent&lt;/th&gt;
      &lt;th&gt;id&lt;/th&gt;
      &lt;th&gt;parent_id&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;/&lt;/td&gt;
      &lt;td&gt;/&lt;/td&gt;
      &lt;td&gt;&amp;nbsp; &lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;&amp;nbsp; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;foo&lt;/td&gt;
      &lt;td&gt;/foo&lt;/td&gt;
      &lt;td&gt;/&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;bar&lt;/td&gt;
      &lt;td&gt;/foo/bar&lt;/td&gt;
      &lt;td&gt;/foo&lt;/td&gt;
      &lt;td&gt;3&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;under foo&lt;/td&gt;
      &lt;td&gt;/foo/under foo&lt;/td&gt;
      &lt;td&gt;/foo&lt;/td&gt;
      &lt;td&gt;5&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;baz&lt;/td&gt;
      &lt;td&gt;/foo/baz&lt;/td&gt;
      &lt;td&gt;/foo&lt;/td&gt;
      &lt;td&gt;8&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;baz.html&lt;/td&gt;
      &lt;td&gt;/foo/bar/baz.html&lt;/td&gt;
      &lt;td&gt;/foo/bar&lt;/td&gt;
      &lt;td&gt;4&lt;/td&gt;
      &lt;td&gt;3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;under bar&lt;/td&gt;
      &lt;td&gt;/foo/bar/under bar&lt;/td&gt;
      &lt;td&gt;/foo/bar&lt;/td&gt;
      &lt;td&gt;6&lt;/td&gt;
      &lt;td&gt;3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;under bar 2&lt;/td&gt;
      &lt;td&gt;/foo/bar/under bar 2&lt;/td&gt;
      &lt;td&gt;/foo/bar&lt;/td&gt;
      &lt;td&gt;7&lt;/td&gt;
      &lt;td&gt;3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;bazzle.html&lt;/td&gt;
      &lt;td&gt;/foo/baz/bazzle.html&lt;/td&gt;
      &lt;td&gt;/foo/baz&lt;/td&gt;
      &lt;td&gt;9&lt;/td&gt;
      &lt;td&gt;8&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;


&lt;p&gt;Looks like it worked! But now that we have this statement it seems like it would be handy to make it a function so you can find the descendants of any particular node. The path might not look right but at least you could get relative children. Let&amp;rsquo;s try it:&lt;/p&gt;

&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;mac_classic&quot;&gt;
&lt;span class=&quot;Keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;FunctionName&quot;&gt;children_of&lt;/span&gt;(root_id &lt;span class=&quot;Keyword&quot;&gt;INTEGER&lt;/span&gt;) 
RETURNS TABLE (name &lt;span class=&quot;Keyword&quot;&gt;VARCHAR&lt;/span&gt;, &lt;span class=&quot;Keyword&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;VARCHAR&lt;/span&gt;, parent &lt;span class=&quot;Keyword&quot;&gt;VARCHAR&lt;/span&gt;, id &lt;span class=&quot;Keyword&quot;&gt;INTEGER&lt;/span&gt;, parent_id &lt;span class=&quot;Keyword&quot;&gt;INTEGER&lt;/span&gt;)
AS $$
WITH RECURSIVE &lt;span class=&quot;Keyword&quot;&gt;path&lt;/span&gt;(name, &lt;span class=&quot;Keyword&quot;&gt;path&lt;/span&gt;, parent, id, parent_id) AS (
  &lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt; name, &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;/'&lt;/span&gt;, &lt;span class=&quot;Keyword&quot;&gt;NULL&lt;/span&gt;, id, parent_id &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; fs &lt;span class=&quot;Keyword&quot;&gt;WHERE&lt;/span&gt; id = $&lt;span class=&quot;Number&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;UNION&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt;
    fs.name, 
    parentpath.&lt;span class=&quot;Keyword&quot;&gt;path&lt;/span&gt; || 
      CASE parentpath.&lt;span class=&quot;Keyword&quot;&gt;path&lt;/span&gt; 
        WHEN &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;/'&lt;/span&gt; THEN &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;'&lt;/span&gt; 
        ELSE &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;/'&lt;/span&gt; 
      END || fs.name,
    parentpath.&lt;span class=&quot;Keyword&quot;&gt;path&lt;/span&gt;, fs.id, fs.parent_id
  &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; fs, &lt;span class=&quot;Keyword&quot;&gt;path&lt;/span&gt; as parentpath
  &lt;span class=&quot;Keyword&quot;&gt;WHERE&lt;/span&gt; fs.parent_id = parentpath.id)
&lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt; * &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;path&lt;/span&gt;;
$$ LANGUAGE &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;sql'&lt;/span&gt;;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;All I've done here is wrap the previous statement with some stuff to make it into a function and replaced 1 with $1, which makes the value the first parameter of the function call. For extra convenience, if I were using this as the foundation of a CMS or something where I would expect to need the path frequently, I might also make a view:&lt;/p&gt;

&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;mac_classic&quot;&gt;
&lt;span class=&quot;Keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;VIEW&lt;/span&gt; &lt;span class=&quot;FunctionName&quot;&gt;paths&lt;/span&gt; AS &lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt; * &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; children_of(&lt;span class=&quot;Number&quot;&gt;1&lt;/span&gt;);
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;It&amp;rsquo;s worth noting that recursive queries probably do not have excellent performance characteristics (I'm not certain but it seems likely.) If you do run into performance problems with this, you might consider looking into (materialized views)[http://tech.jonathangardner.net/wiki/PostgreSQL/Materialized_Views] which can be implemented manually in PostgreSQL using triggers.&lt;/p&gt;

&lt;p&gt;If you prefer, you could instead create a function to find the path of a given ID:&lt;/p&gt;

&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;mac_classic&quot;&gt;
&lt;span class=&quot;Keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;FUNCTION&lt;/span&gt; &lt;span class=&quot;FunctionName&quot;&gt;pathname&lt;/span&gt;(id &lt;span class=&quot;Keyword&quot;&gt;INTEGER&lt;/span&gt;) RETURNS &lt;span class=&quot;Keyword&quot;&gt;VARCHAR&lt;/span&gt; AS $$
&lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;/'&lt;/span&gt; || &lt;span class=&quot;Keyword&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt;
(WITH RECURSIVE pathto(&lt;span class=&quot;Keyword&quot;&gt;path&lt;/span&gt;, id) AS (
  &lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt; name, parent_id &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; fs &lt;span class=&quot;Keyword&quot;&gt;WHERE&lt;/span&gt; id = $&lt;span class=&quot;Number&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;UNION&lt;/span&gt;
  &lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt; fs.name || &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;/'&lt;/span&gt; || pathto.&lt;span class=&quot;Keyword&quot;&gt;path&lt;/span&gt;, parent_id
  &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; fs, pathto &lt;span class=&quot;Keyword&quot;&gt;WHERE&lt;/span&gt; fs.id = pathto.id)
&lt;span class=&quot;Keyword&quot;&gt;SELECT&lt;/span&gt; * &lt;span class=&quot;Keyword&quot;&gt;FROM&lt;/span&gt; pathto) AS pathto(&lt;span class=&quot;Keyword&quot;&gt;path&lt;/span&gt;, id)
&lt;span class=&quot;Keyword&quot;&gt;WHERE&lt;/span&gt; id = &lt;span class=&quot;Number&quot;&gt;1&lt;/span&gt;;
$$ LANGUAGE &lt;span class=&quot;String&quot;&gt;&lt;span class=&quot;String&quot;&gt;'&lt;/span&gt;sql'&lt;/span&gt;;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;I was then tempted to make an index on this function, but PostgreSQL won&amp;rsquo;t allow computed indices using functions not marked &lt;code&gt;IMMUTABLE&lt;/code&gt;, which is a way of certifying to PostgreSQL that it is a pure function. Because this function will return different values depending on the content of a table, it isn&amp;rsquo;t immutable, and the index could get out-of-sync with reality without PostgreSQL knowing it. Intuitively, if you rename a node, all of its children will then have different paths reflecting the new name but it&amp;rsquo;s unclear how you could make PostgreSQL aware of that fact.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Plan 9's Advantage</title>
    <link href="/blog/2009/08/07/plan-9s-advantage.html" />
    <id>tag:storytotell.org,2009-08-07:1249632067</id>
    <updated>2009-08-07T02:01:07-06:00</updated>
    <content type="html">&lt;p&gt;Plan 9 is Unix sans castration.&lt;/p&gt;

&lt;p&gt;The central idea behind Plan 9 is that a filesystem is a dandy interface to all kinds of things. Take a look at &lt;a href=&quot;http://plan9.bell-labs.com/sources/plan9/sys/src/libc/9syscall/sys.h&quot;&gt;the list of system calls in Plan 9&lt;/a&gt;, all 51 of them.&lt;/p&gt;

&lt;p&gt;What kinds of things can be filesystems that typically aren&amp;rsquo;t? All your processes—post a note (remember signals in Unix?) by writing to their control file, see what files they have open, etc. Your network interface. Need NAT? Mount the gateway&amp;rsquo;s network devices locally. Get a connection and listen or connect to a remote host by opening a file and writing to it. Access a database through the filesystem. Access the web through the filesystem. Access FTP or any other remote filesystem through the local filesystem. Manipulate this window on the screen through the filesystem. Draw graphics through the filesystem. Access your keychain through the filesystem. Access your sound card through the filesystem.&lt;/p&gt;

&lt;p&gt;Exposing everything through the filesystem has three principle benefits:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Now any aspect of your machine&amp;rsquo;s hardware, display, or any running software can be accessed from other places on the network. Or vice versa.&lt;/li&gt;
&lt;li&gt;Want to improve security by jailing a process? Remove the interfacing files from the namespace of that process.&lt;/li&gt;
&lt;li&gt;Access to your abstraction is democratized and language-agnostic.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;The first two points get made a lot in Plan 9 circles and are the backbone of a secure distributed environment. But the third point is one that&amp;rsquo;s been fascinating me. The echo service on Plan 9 is a shell script that runs &lt;code&gt;cat&lt;/code&gt;. You can use regular shell scripts to connect programs and devices across machines. If you find a filesystem that provides access to a given device, protocol, or whatever, every language on your machine gets uniform access to that device, protocol or whatever without any bias towards a particular one.&lt;/p&gt;

&lt;p&gt;This also means that in Plan 9, writing a filesystem is better in many cases than writing a library. A library can only be used from C; a filesystem can be used from C, the shell, &lt;code&gt;awk&lt;/code&gt; or any other language. Furthermore, a filesystem can be exported over the network or imported from another host. Hooking up a file to another file doesn&amp;rsquo;t sound very interesting, but if one file is the output of a progress bar application and the other is your soundcard&amp;rsquo;s control file, it becomes interesting. Or if one file is your mail and the other is your Jabber account, and between them is a little AWK script that pulls out the subject. Or if one file is your hard drive&amp;rsquo;s SMART status and the other is your SMS gateway. The mere thought of being able to combine these things with trivial shell scripts in the grand Unix tradition is quite alluring.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>How to Manage a Website, Destructively</title>
    <link href="/blog/2009/07/13/how-to-manage-a-website-destructively.html" />
    <id>tag:storytotell.org,2009-07-13:1247466597</id>
    <updated>2009-07-13T00:29:57-06:00</updated>
    <content type="html">&lt;p&gt;For a little while I was thinking about managing my website with this before I decided to use Webby instead. I changed my mind for three reasons:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;My system sucks at syntax highlighting. With Webby, you just use &lt;a href=&quot;http://ultraviolet.rubyforge.org/&quot;&gt;Ultraviolet&lt;/a&gt;.&lt;/li&gt;
	&lt;li&gt;There isn&amp;#8217;t a good way to do escape characters in m4, so some blog posts break spontaneously.&lt;/li&gt;
	&lt;li&gt;Webby somehow manages to be faster.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I&amp;#8217;m not sure I&amp;#8217;m going to abandon this idea completely though. It might be a good project to do with Plan 9. If I remake the site in Clojure then I probably will go for something more dynamic though.&lt;/p&gt;
&lt;p&gt;Anyway.&lt;/p&gt;
&lt;h3&gt;Conception&lt;/h3&gt;
&lt;p&gt;This project is a hate crime.&lt;/p&gt;
&lt;p&gt;I hate websites. Especially I hate software for managing them which is too limited and doesn&amp;#8217;t build on the fundamental flexibility of the platform. I also hate reinventing the wheel.&lt;/p&gt;
&lt;p&gt;It occurred to me as I was playing with &lt;a href=&quot;http://webby.rubyforge.org/&quot;&gt;Webby&lt;/a&gt; that I hate &lt;a href=&quot;http://rake.rubyforge.org/&quot;&gt;Rake&lt;/a&gt;, because it&amp;#8217;s slower than &lt;a href=&quot;http://www.gnu.org/software/make/&quot;&gt;real make&lt;/a&gt; and less powerful and because I hate Ruby now too.&lt;/p&gt;
&lt;p&gt;Why not use make? Make has a great advantage over other systems: you can write pattern rules that generate some file type from some other file type and now the whole thing knows how to transform all your files from one thing to another. That&amp;#8217;s handy.&lt;/p&gt;
&lt;p&gt;Thinking about what everyone needs in a website, they really only need a system to get some text into a giant ball of HTML and stuff that into their template somehow. Perhaps also doing some other template-like crap at the same time. There are lots of great tools for managing your HTML glorp, unfortunately many of them are Ruby specific, but there&amp;#8217;s &lt;a href=&quot;http://www.textism.com/tools/textile/&quot;&gt;Textile&lt;/a&gt;, &lt;a href=&quot;http://daringfireball.net/projects/markdown/&quot;&gt;Markdown&lt;/a&gt;, &lt;a href=&quot;http://haml.hamptoncatlin.com/&quot;&gt;Haml&lt;/a&gt; and others.&lt;/p&gt;
&lt;p&gt;Interestingly, you only really want to deal with Haml for the template and Textile or Markdown for the structure. Textile looks more like plain text to me than Markdown but Markdown is better at dealing with code and nested stuff&amp;mdash;I seem to have to resort to raw HTML a lot less, and it&amp;#8217;s bi-directional. I like having choices.&lt;/p&gt;
&lt;p&gt;So this seemed like my formula: glorp + template = HTML.&lt;/p&gt;
&lt;h3&gt;A Twist&lt;/h3&gt;
&lt;p&gt;How do you deal with things like the title? The title ostensibly appears in the glorp somewhere but it also needs to wind up at the top of the file, in the &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;
&lt;p&gt;This seems like metadata to me. So the new formula looks like this: glorp + metadata + template = HTML.&lt;/p&gt;
&lt;h3&gt;A Taste&lt;/h3&gt;
&lt;ol&gt;
	&lt;li&gt;&amp;lsquo;bin/build-rules.sh sites&lt;/li&gt;
	&lt;li&gt;make&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;
    [fusion@Anatolia Makesite] % make rsync
    haml template.haml &amp;gt; template.m4.pre
    cat header.m4 template.m4.pre &amp;gt; template.m4
    rm template.m4.pre
    redcloth site/about-me.textile &amp;gt; site/about-me.phtml
    ruby bin/metadata.rb site/about-me.phtml &amp;gt; site/about-me.meta.m4
    m4 -D __metadata__=site/about-me.meta.m4 -D __body__=site/about-me.phtml template.m4 -D __dir__=site &amp;gt; site/about-me.html
    ruby bin/webby2ms.rb m4 site/how-it-works.webby &amp;gt; site/how-it-works.meta.m4
    ruby bin/webby2ms.rb content site/how-it-works.webby &amp;gt; site/how-it-works.textile
    redcloth site/how-it-works.textile &amp;gt; site/how-it-works.phtml
    m4 -D __metadata__=site/how-it-works.meta.m4 -D __body__=site/how-it-works.phtml template.m4 -D __dir__=site &amp;gt; site/how-it-works.html
    perl bin/Markdown.pl --html4tags site/index.markdown &amp;gt; site/index.phtml
    ruby bin/metadata.rb site/index.phtml &amp;gt; site/index.meta.m4
    m4 -D __metadata__=site/index.meta.m4 -D __body__=site/index.phtml template.m4 -D __dir__=site &amp;gt; site/index.html
    cp -r static output
    bin/install.sh output  site/about-me.html site/how-it-works.html site/index.html
    site/about-me.html -&amp;gt; output//about-me.html
    site/how-it-works.html -&amp;gt; output//how-it-works.html
    site/index.html -&amp;gt; output//index.html
    rsync -vrzp -e ssh --chmod=u+rwX,go+rX output/* clanspum.net:sites/beta.storytotell.org
    sending incremental file list
    about-me.html
    how-it-works.html
    index.html
    css/style.css
    downloads/
    downloads/makesite.tar.gz
    images/.DS_Store
    images/accept.png
    images/accept50.png
    images/bg_body.png
    images/bullet_bottom.png
    images/bullet_right.png
    images/content_bg.png
    images/email_open.png
    images/email_open50.png
    images/h1_bubble_bg.png
    images/h2_bubble_bg.png
    images/meta.png
    images/page_edit.png
    images/page_edit50.png
    images/sidebar_section_bg.png
    images/sidebar_section_bg_over.png
    images/tag.png
    images/title_bg.png
    images/user.png
    images/user50.png
    images/world_link.png
    images/world_link50.png
    
    sent 94105 bytes  received 1851 bytes  38382.40 bytes/sec
    total size is 234097  speedup is 2.44
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Templates&lt;/h3&gt;
&lt;p&gt;For the final substitution, I don&amp;#8217;t need much; all I really need is a way to substitute some metadata variables and swap in the content into the big hole in the middle of the template. Suddenly I remembered &lt;a href=&quot;http://www.gnu.org/software/m4/&quot;&gt;M4&lt;/a&gt;, the ancient Unix macro processor. Yeah, it&amp;#8217;s fugly and gross, but like make, it isn&amp;#8217;t going anywhere and it&amp;#8217;s definitely powerful enough.&lt;/p&gt;
&lt;h3&gt;Using make&lt;/h3&gt;
&lt;p&gt;If you&amp;#8217;re going to use make effectively, you need to figure out how to encode your information in the actual filename of the file. Make is going to use the extension of your file to figure out what to do with it.&lt;/p&gt;
&lt;p&gt;Since I&amp;#8217;m using m4, I decided to make a separate file for the metadata and make it m4 format. This is the extension &lt;code&gt;.meta.m4&lt;/code&gt;. Also, pre-processed HTML has the extension &lt;code&gt;.phtml&lt;/code&gt;. Now all I had to do was come up with the m4 command line to process a template with the body and the metadata. I came up with this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;%.html: %.meta.m4 %.phtml template.m4
	m4 -D __metadata__=$&amp;lt; -D __body__=$(word 2,$+) $(lastword $+) -D __dir__=$(dir) &amp;gt; $@&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I decided to use &lt;i&gt;foo&lt;/i&gt; for all my m4 variables to reduce the chances that it runs into some text I&amp;#8217;ve already defined in my files. &lt;i&gt;body&lt;/i&gt; is the filename of the HTML glorp, &lt;i&gt;metadata&lt;/i&gt; is the filename of the metadata and &lt;i&gt;dir&lt;/i&gt; is the path we&amp;#8217;re at when we invoke m4 (important for calculating relative paths).&lt;/p&gt;
&lt;h3&gt;Template Structure&lt;/h3&gt;
&lt;p&gt;Our template must begin with this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;include(site/header.m4)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These two lines do two things: import the metadata file using the variable from the command line and define the relative path back to the root (handy for links to CSS and whatnot in the base of the site).&lt;/p&gt;
&lt;p&gt;Later on I decided that template.m4 ought to be built out of parts, I extracted this to header.m4 and define template.m4 to be built out of Haml using this rule:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template.m4: template.haml
	haml $&amp;lt; &amp;gt; $@.pre
	cat header.m4 $@.pre &amp;gt; $@
	rm $@.pre&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now I can just use my m4 variables from within Haml instead of worrying about filling in the template with Ruby! :D&lt;/p&gt;
&lt;h3&gt;Making Glorp&lt;/h3&gt;
&lt;p&gt;To make the HTML glorp, I have two patterns for handling textile and markdown:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;%.phtml: %.textile
	redcloth $&amp;lt; &amp;gt; $@

%.phtml: %.markdown bin/Markdown.pl
	perl bin/Markdown.pl --html4tags $&amp;lt; &amp;gt; $@&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(I have the markdown depend on the Perl file because it&amp;#8217;s in the distribution and may change.)&lt;/p&gt;
&lt;p&gt;Both of these just render the text and produce the interior HTML.&lt;/p&gt;
&lt;h3&gt;Handling Webby&lt;/h3&gt;
&lt;p&gt;I had already written a utility to convert my Typo blog posts to Webby format. Webby&amp;#8217;s format is interesting; basically it&amp;#8217;s a YAML wrapper on top of plain text. The wrapper specifies the format of the text and the YAML contains the metadata. So at this point I created a utility just to deal with Webby files and renamed all of them to end in &lt;code&gt;.webby&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The utility takes a file and a single command, which is either &amp;#8216;m4&amp;#8217; or &amp;#8216;content&amp;#8217; depending on whether it&amp;#8217;s generating the content or the m4 metadata. To make the content, it just slices it out and prints it; to make the metadata it parses the YAML and converts it to a series of &lt;code&gt;define()&lt;/code&gt; statements in m4.&lt;/p&gt;
&lt;p&gt;Make was informed:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;%.meta.m4: %.webby bin/webby2ms.rb
	ruby bin/webby2ms.rb m4 $&amp;lt; &amp;gt; $@

%.textile: %.webby bin/webby2ms.rb
	ruby bin/webby2ms.rb content $&amp;lt; &amp;gt; $@&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In a future version, it might be able to determine the file type to create, but for right now it was convenient just to specify Textile since all my old blog posts are in Textile anyway.&lt;/p&gt;
&lt;h3&gt;Generic Metadata&lt;/h3&gt;
&lt;p&gt;It occurred to me that you could find some metadata just by introspecting the file, such as to look for the first h2/3/4 for the title or the ctime of the file for the creation time. So I wrote a short script to create a &lt;code&gt;.meta.m4&lt;/code&gt; from a &lt;code&gt;.phtml&lt;/code&gt;. It&amp;#8217;s important to keep the Webby lines above these rules or it will use these rules to produce &lt;code&gt;.meta.m4&lt;/code&gt; from &lt;code&gt;.webby&lt;/code&gt; files when there is a better method available.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;%.meta.m4: %.phtml bin/metadata.rb
	ruby bin/metadata.rb $&amp;lt; &amp;gt; $@&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Non-Recursive Make Structure&lt;/h3&gt;
&lt;p&gt;At this point, I could generate whatever html file I wanted from my templates and my old content, but I&amp;#8217;d have to ask for it by name. I needed some kind of make rules that would find all the old content and build it.&lt;/p&gt;
&lt;p&gt;As it turns out, there&amp;#8217;s still recursion in non-recursive make, it&amp;#8217;s just recursion of reading make files and processing their contents before doing all the work.&lt;/p&gt;
&lt;p&gt;First of all we need to understand how to invoke make non-recursively. I read the famous paper, &lt;a href=&quot;http://cj5.info/pmiller/books/rmch/&quot;&gt;Recursive Make Considered Harmful&lt;/a&gt; and ran into it the other day and saw the link off to &lt;a href=&quot;http://www.xs4all.nl/~evbergen/nonrecursive-make.html&quot;&gt;Emile van Bergen&amp;#8217;s implementation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I had to follow this in a very regimented fashion, so I wrote a script which recursively builds Rules.mk files for all of the subdirectories under some directory. This is very tailored to this app and will need to be extensively overhauled if I want to generate something other than HTML or use it in another app. But it&amp;#8217;s also quite short and illustrates recursive calling in the shell.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/zsh

# bring in the header
function build_rules() {
	...
}

function build_rules_rec() {
	build_rules $1 &amp;gt; $1/Rules.mk

	for dir in $(find $1 -type d -maxdepth 1 | sed '1d'); do
		build_rules_rec $dir &amp;gt; $dir/Rules.mk
	done	
}

build_rules_rec $1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The essence of build_rules is that it looks in a directory and creates recursive loading of Rules.mk in each of the subdirectories, and adds all of the .webby files in the current directory to $(TGTS_HTML) with the extension changed. We&amp;#8217;re being declarative here, folks.&lt;/p&gt;
&lt;h3&gt;Rock and Roll&lt;/h3&gt;
&lt;p&gt;The rest of the makefile is pretty much cake; define all to be targets, define targets to be $(TGTS_HTML), define clean to be rm -f $(CLEAN). Run make -j4 and watch as your website gets built &lt;em&gt;with SMP!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Of course now you have the problem of installation. I didn&amp;#8217;t want the installation method to be decoupled from the list of targets we&amp;#8217;re building so I wound up writing a shell script which does some path nastiness to copy the target files preserving their path parts into the output directory. The output directory is first made by making a copy of a &lt;code&gt;static&lt;/code&gt; folder which contains things that don&amp;#8217;t need to be expanded like images and CSS files.&lt;/p&gt;
&lt;p&gt;Then I added an install target and an rsync target which copies it up to the website.&lt;/p&gt;
&lt;p&gt;That&amp;#8217;s it in a nutshell.&lt;/p&gt;
&lt;h3&gt;The Code&lt;/h3&gt;
&lt;p&gt;I&amp;#8217;m sure you&amp;#8217;re all quite desperate to give this a try. &lt;a href=&quot;/blog/2009/07/13/makesite.tar.gz&quot;&gt;Download the source&lt;/a&gt; and hopefully it&amp;#8217;ll still be working for you then.&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>On Optimization</title>
    <link href="/blog/2009/07/10/on-optimization.html" />
    <id>tag:storytotell.org,2009-07-10:1247260929</id>
    <updated>2009-07-10T15:22:09-06:00</updated>
    <content type="html">&lt;p&gt;Frequently on the Clojure list somebody shows up and wants to know what&amp;rsquo;s the most optimal way to do something, you know, without any of that costly functional stuff, just pure speed. I have a feeling this comes up in other forums too so I thought I'd spend a minute and give the world my nasty opinion about optimization.&lt;/p&gt;

&lt;p&gt;Every excellent programmer knows that correctness is &lt;em&gt;harder&lt;/em&gt; to achieve than performance &lt;em&gt;and&lt;/em&gt; more important. You can&amp;rsquo;t profile unfinished code. Optimally performing code is also usually larger and less clear&amp;mdash;which means it has more places to hide bugs. It&amp;rsquo;s also usually less flexible. If possible, you'd rather have an obviously correct function to compare results with as you&amp;rsquo;re making an optimal version (I consider this more useful than unit tests, because it can generate unit tests).&lt;/p&gt;

&lt;p&gt;Performance worrying is the last refuge of the lousy. Nobody takes broken but fast code over correct code, unless they don&amp;rsquo;t care about the program. Optimizing correct code is easy. You find the two lines that are slow with your profiler and fix them. This is much easier when you haven&amp;rsquo;t pre-written optimal-looking but enormous code. You have less code to change.&lt;/p&gt;

&lt;p&gt;Optimizing code before you absolutely have to is also a violation of YAGNI. Great programmers don&amp;rsquo;t just spill out reams and reams of code, they also work hard to contain the codebase and remove redundant code. If my two line function performs within a factor of three of your ten line function, mine wins &lt;em&gt;until&lt;/em&gt; you have proof that it is &lt;strong&gt;the&lt;/strong&gt; bottleneck in this program.&lt;/p&gt;

&lt;p&gt;And yes, every program has a bottleneck. But for most programs ordinary folk like us are going to write, that bottleneck is actually I/O or the network.&lt;/p&gt;

&lt;p&gt;I once worked on a program that had to deal with building a bracket for playoffs in various sports. I spent a little time thinking about the problem and how I wanted to approach it and realized it&amp;rsquo;s just a binary tree and pulled out my log base 2s. By some fluke, the other programmer hadn&amp;rsquo;t thought of this yet and was just going to hard code it for 16 or 32 teams. Hard-coding it would definitely have been more lines of code, slower, and less flexible.&lt;/p&gt;

&lt;p&gt;The major, overlooked way to achieve better performance in all your software is to do less work. In order to do less work you have to understand your tools and your fundamentals. Math is in your fundamentals! You should understand algebra, logarithms, a little calculus, a little probability, if you want to program well. SQL is one of your tools! Nine times out of ten when a webapp performs poorly it&amp;rsquo;s because of the N+1 query problem and it can be fixed with better, more intelligent SQL. If you&amp;rsquo;re storing data in a relational database, you should use it like a relational database rather than an object graph! If you want to store an object graph, find an OODB and make that your back end! Your performance and your LOC will thank you.&lt;/p&gt;

&lt;p&gt;To sum up:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Think about the problem. Try to avoid doing some or all of the work. Use math!&lt;/li&gt;
&lt;li&gt;If you have to do it, write the code as clearly and correctly as you can&lt;/li&gt;
&lt;li&gt;See if you notice an actual performance problem.&lt;/li&gt;
&lt;li&gt;Profile the code.&lt;/li&gt;
&lt;li&gt;Rewrite the critical section. Preferably, it&amp;rsquo;s already a function and you can swap this in as the new version.&lt;/li&gt;
&lt;li&gt;Ensure that your new version returns the same results as the old.&lt;/li&gt;
&lt;li&gt;Don&amp;rsquo;t touch anything unrelated to the performance problem.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;If you follow this pattern, you&amp;rsquo;ll get more done, your code will be higher quality and it will perform well with only a few minor sacrifices, and you won&amp;rsquo;t lose correctness.&lt;/p&gt;

&lt;p&gt;Bonus tip: Don&amp;rsquo;t fight the language! Don&amp;rsquo;t write Java in PHP, or your PHP&amp;rsquo;s performance will go to hell. Don&amp;rsquo;t write C in Java or Java&amp;rsquo;s performance will go to hell. Don&amp;rsquo;t write Java in Clojure or Clojure&amp;rsquo;s performance will go to hell. Use the language the way it was meant to be used: you&amp;rsquo;ll hit all the paths that the implementor has optimized rather than the weird corner cases he hasn&amp;rsquo;t anyway. Plus, usually the extra work you do to try and obtain better performance will wind up costing more than just doing it the easy way. And don&amp;rsquo;t even start with this without profiling!&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>On Prolog</title>
    <link href="/blog/2009/07/10/on-prolog.html" />
    <id>tag:storytotell.org,2009-07-10:1247259799</id>
    <updated>2009-07-10T15:03:19-06:00</updated>
    <content type="html">&lt;p&gt;For a couple days this week I was working on a clone of &lt;a href=&quot;http://www.freebsd.org/cgi/man.cgi?query=bc&amp;amp;apropos=0&amp;amp;sektion=0&amp;amp;manpath=FreeBSD+7.2-RELEASE&amp;amp;format=html&quot;&gt;bc(1)&lt;/a&gt; in Prolog.&lt;/p&gt;

&lt;p&gt;My original thought was that this would be quite perverted and cool, because you can do some interesting things with Prolog, like &lt;a href=&quot;/blog/2009/07/08/playing-with-dcg.html&quot;&gt;DCG&amp;rsquo;s&lt;/a&gt; and you can create and destructure Prolog terms pretty easily. For example:&lt;/p&gt;

&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;mac_classic&quot;&gt;
?- &lt;span class=&quot;UserDefinedConstant&quot;&gt;foo&lt;/span&gt;(&lt;span class=&quot;UserDefinedConstant&quot;&gt;bar&lt;/span&gt;,&lt;span class=&quot;UserDefinedConstant&quot;&gt;baz&lt;/span&gt;) =.. &lt;span class=&quot;Variable&quot;&gt;X&lt;/span&gt;.
&lt;span class=&quot;Variable&quot;&gt;X&lt;/span&gt; = [&lt;span class=&quot;UserDefinedConstant&quot;&gt;foo&lt;/span&gt;, &lt;span class=&quot;UserDefinedConstant&quot;&gt;bar&lt;/span&gt;, &lt;span class=&quot;UserDefinedConstant&quot;&gt;baz&lt;/span&gt;].

?- &lt;span class=&quot;Variable&quot;&gt;Y&lt;/span&gt; =.. [&lt;span class=&quot;UserDefinedConstant&quot;&gt;foo&lt;/span&gt;, &lt;span class=&quot;UserDefinedConstant&quot;&gt;bar&lt;/span&gt;, &lt;span class=&quot;UserDefinedConstant&quot;&gt;baz&lt;/span&gt;].
&lt;span class=&quot;Variable&quot;&gt;Y&lt;/span&gt; = &lt;span class=&quot;UserDefinedConstant&quot;&gt;foo&lt;/span&gt;(&lt;span class=&quot;UserDefinedConstant&quot;&gt;bar&lt;/span&gt;, &lt;span class=&quot;UserDefinedConstant&quot;&gt;baz&lt;/span&gt;).
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The key here is the &lt;code&gt;=..&lt;/code&gt; operator, which unifies a list of terms on the right with a Prolog-style term on the left. I was hoping to have a DCG that looked something like this:&lt;/p&gt;

&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;mac_classic&quot;&gt;
&lt;span class=&quot;UserDefinedConstant&quot;&gt;term&lt;/span&gt;(&lt;span class=&quot;Variable&quot;&gt;Expr&lt;/span&gt;) --&amp;gt; &amp;quot;(&amp;quot;, &lt;span class=&quot;UserDefinedConstant&quot;&gt;whitespace&lt;/span&gt;, &lt;span class=&quot;UserDefinedConstant&quot;&gt;expr&lt;/span&gt;(&lt;span class=&quot;Variable&quot;&gt;Expr&lt;/span&gt;), &lt;span class=&quot;UserDefinedConstant&quot;&gt;whitespace&lt;/span&gt;, &amp;quot;)&amp;quot;.
&lt;span class=&quot;UserDefinedConstant&quot;&gt;term&lt;/span&gt;(-&lt;span class=&quot;Variable&quot;&gt;Expr1&lt;/span&gt;) --&amp;gt; &amp;quot;-&amp;quot;, &lt;span class=&quot;UserDefinedConstant&quot;&gt;whitespace&lt;/span&gt;, &lt;span class=&quot;UserDefinedConstant&quot;&gt;expr&lt;/span&gt;(&lt;span class=&quot;Variable&quot;&gt;Expr1&lt;/span&gt;).
&lt;span class=&quot;UserDefinedConstant&quot;&gt;term&lt;/span&gt;(&lt;span class=&quot;Variable&quot;&gt;N&lt;/span&gt;) --&amp;gt; &lt;span class=&quot;UserDefinedConstant&quot;&gt;number&lt;/span&gt;(&lt;span class=&quot;Variable&quot;&gt;N&lt;/span&gt;).

&lt;span class=&quot;UserDefinedConstant&quot;&gt;expr&lt;/span&gt;(&lt;span class=&quot;Variable&quot;&gt;Left&lt;/span&gt; ^ &lt;span class=&quot;Variable&quot;&gt;Right&lt;/span&gt;) --&amp;gt; &lt;span class=&quot;UserDefinedConstant&quot;&gt;powterm&lt;/span&gt;(&lt;span class=&quot;Variable&quot;&gt;Left&lt;/span&gt;), &lt;span class=&quot;UserDefinedConstant&quot;&gt;whitespace&lt;/span&gt;, &amp;quot;^&amp;quot;, &lt;span class=&quot;UserDefinedConstant&quot;&gt;whitespace&lt;/span&gt;, &lt;span class=&quot;UserDefinedConstant&quot;&gt;powterm&lt;/span&gt;(&lt;span class=&quot;Variable&quot;&gt;Right&lt;/span&gt;).
&lt;span class=&quot;Comment&quot;&gt;&lt;span class=&quot;Comment&quot;&gt;%&lt;/span&gt; and so forth&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;Problem is, Prolog&amp;rsquo;s DCGs don&amp;rsquo;t really want to do this and I found a &lt;a href=&quot;http://www.nabble.com/DCG-for-arithmetic-expression-(right-recursion-problem)-td21734023.html&quot;&gt;mailing list article about it&lt;/a&gt; that took the wind right out of my sails. Basically, it says, to do this you can&amp;rsquo;t use a DCG without passing along some extra information.&lt;/p&gt;

&lt;p&gt;Anyway, I just wanted to share that for some reason. I have a grammar right now that looks like it should be working, but it has associativity backwards. To wit:&lt;/p&gt;

&lt;div class=&quot;UltraViolet&quot;&gt;
&lt;pre class=&quot;mac_classic&quot;&gt;
&amp;gt; 2 ^ 1 + 1 * 2
2^ ((1+1)*2)
16
&lt;/pre&gt;
&lt;/div&gt;


&lt;p&gt;The correct answer would be (2^1) + (1*2).&lt;/p&gt;

&lt;p&gt;If any of you want to take a crack at this and brighten my day, &lt;a href=&quot;blog/2009/07/10/bc.pl&quot;&gt;the code&amp;rsquo;s attached&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Thoughts About Plan 9 and Inferno</title>
    <link href="/blog/2009/07/10/thoughts-about-plan-9-and-inferno.html" />
    <id>tag:storytotell.org,2009-07-10:1247258302</id>
    <updated>2009-07-10T14:38:22-06:00</updated>
    <content type="html">&lt;p&gt;I've been thinking about paradigms of programming lately. Prolog always seems to bring that to mind and I'm not sure why, but Prolog has been on my mind. And Plan 9 and Inferno.&lt;/p&gt;

&lt;p&gt;There&amp;rsquo;s an interesting &lt;a href=&quot;http://agni.csa.iisc.ernet.in/OperatingSystems/Inferno/infernojava/infernojava.html&quot;&gt;comparison between Inferno and Java&lt;/a&gt; I looked at. It seems misguided to compare these two systems but perhaps they should be compared. They differ at the very lowest level, as in, what is the virtual part of this virtual machine? Java uses the VM strictly as an execution architecture for a single program. Inferno gives you a whole OS on top of that VM; you get multiple processes, each potentially having multiple threads. Yet it seems to somehow be thinner than Java in many respects.&lt;/p&gt;

&lt;p&gt;Inferno is really just another way to run Plan 9, just a friendlier, sneaky way of getting people who use normal OSes to use it. Of course it can run on the bare hardware too. Which means there&amp;rsquo;s really six ways to run Plan 9:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Plan 9 on bare hardware&lt;/li&gt;
&lt;li&gt;Plan 9 from Userspace + Unix of your choice&lt;/li&gt;
&lt;li&gt;9vx, user-mode virtualized Plan 9&lt;/li&gt;
&lt;li&gt;Inferno on bare hardware&lt;/li&gt;
&lt;li&gt;Inferno under the OS of your choice&lt;/li&gt;
&lt;li&gt;Glendix (port of Plan 9 userland to Linux)&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;And of course that&amp;rsquo;s omitting traditional virtualization software like &lt;a href=&quot;http://www.virtualbox.org/&quot;&gt;VirtualBox&lt;/a&gt; and Parallels.&lt;/p&gt;

&lt;p&gt;I'm suspicious of ideas that come in six or ten boxes. Seems like the idea doesn&amp;rsquo;t really have a home. Which one of those is the preferred way to get the Plan 9 experience? Hard for me to say. I have tried all of the virtual methods I can find and still don&amp;rsquo;t find myself using it, even though I love the theory.&lt;/p&gt;

&lt;p&gt;The other weird thing about Plan 9 is that it&amp;rsquo;s a complex thought. I don&amp;rsquo;t mean the namespacing and 9fs for all servers, that makes sense, it&amp;rsquo;s the whole Unix pantheon that has to come along too. I guess it&amp;rsquo;s a testament to the portability of the idea.&lt;/p&gt;

&lt;p&gt;The last time I looked at the programming language for Inferno, Limbo, I thought it looked really bizarre. This time it looks like C with a big dose of ML and a small dose of C++. The thought of programming in a language with so much syntax in an editor that doesn&amp;rsquo;t support syntax highlighting is pretty intimidating. But nobody seems to be using anything but Acme though or there would be an Emacs mode and a TextMate mode, right? So maybe it&amp;rsquo;s manageable. Presumably the compiler does a great job of telling you how you screwed up.&lt;/p&gt;

&lt;p&gt;Another weird problem for me is the parsing problem. The advantage of networking with XML or JSON is that you already know the object model; you can pretend you&amp;rsquo;re dealing in structured data (objects) instead of bytes. Not that it&amp;rsquo;s particularly hard to write a parser with the old school tools, but is yacc the best we get to have? When I see the &lt;a href=&quot;http://www.vitanuova.com/inferno/man/1/yacc.html&quot;&gt;man page for iyacc&lt;/a&gt; I get a woozy feeling. Plan 9 was made by and for people who thought Unix was perfect at everything except networking. Inferno seems almost to have been made sarcastically to make a version of Plan 9 that competes with Java. But Java tries to solve the whole problem of portability inside a language where the primitive concept is the object, whereas Inferno tries to solve it with Plan 9, which means the whole OS has to come along for the ride.&lt;/p&gt;

&lt;p&gt;I really like the idea of the Plan 9 environment. But to run it on a single computer is a little pointless. Plan 9 is really intended to take a whole network of computers and make it behave like one computer split across a network. You give every user their own Plan 9 box and then set up a big file server for the common filesystems, get a bunch of CPU boxes for running CPU-intensive stuff, you&amp;rsquo;re basically done. You can do cool things like wire up all of the CPU server&amp;rsquo;s audio to go back to yours over the network. Or move the display around on various computers. You could do some really neat stuff. With just one box&amp;hellip; you&amp;rsquo;re already there. It feels a little futile.&lt;/p&gt;

&lt;p&gt;I have the sense that if I just got a couple dedicated computers to run it I could be having a lot of fun and really make some progress. Is it an illusion? Maybe someday I&amp;rsquo;ll find out.&lt;/p&gt;
</content>
  </entry>
  
</feed>
