<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7029981</id><updated>2011-09-29T09:06:43.179-04:00</updated><category term='ocaml'/><title type='text'>more blog than sense</title><subtitle type='html'>Graham decides that he should start a blog for no good reason at all.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://fawcett.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://fawcett.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>fawcett</name><uri>http://www.blogger.com/profile/03670078611251538949</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>25</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7029981.post-6941779367600366649</id><published>2010-02-24T20:41:00.002-05:00</published><updated>2010-02-24T20:52:40.783-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ocaml'/><title type='text'>One-upping the null parser: the amazing Echo language</title><content type='html'>&lt;p&gt;In my &lt;a href="http://fawcett.blogspot.com/2010/02/making-null-language-with-ocaml.html"&gt;previous article&lt;/a&gt;, I offered what I think is the simplest
   parser you can write for &lt;a href="http://caml.inria.fr/"&gt;Ocaml&lt;/a&gt; with &lt;a href="http://pauillac.inria.fr/~ddr/camlp5/"&gt;camlp5&lt;/a&gt; that will actually
   generate compilable code. It was simple because it ignored its input:
   every source program got compiled into the same static executable.
   This time we're taking a big leap upward, into the world where parsers
   actually consume their inputs. In this little language (let's call it
   "echo"), each word in the source is translated into an Ocaml "print
   statement" that outputs that word on its own line. (For clarity, I'm
   also going to stick the word &lt;code&gt;EMIT:&lt;/code&gt; in front of every one of the
   printed lines.) So, the input program &lt;code&gt;hello.src&lt;/code&gt;:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;My fancy
hello-world program.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&amp;hellip;will be compiled to an executable that outputs:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;EMIT: My
EMIT: fancy
EMIT: hello-world
EMIT: program.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The "echo" language is more complex than the null language, and we
   have to organize things a bit differently in order to get a good
   result. Our basic strategy here is to:
&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;
     define a simple &lt;a href="http://en.wikipedia.org/wiki/Lexical_analysis"&gt;lexer&lt;/a&gt;, using the Ocamllex tool
 &lt;/li&gt;

 &lt;li&gt;
     replace &lt;code&gt;camlp5&lt;/code&gt;'s Ocaml-oriented lexer with our own
 &lt;/li&gt;

 &lt;li&gt;
     replace &lt;code&gt;camlp5&lt;/code&gt;'s toplevel parser with our echo-parser
 &lt;/li&gt;

 &lt;li&gt;
     use our echo-parser to compile echo-programs into echo-executables.
 &lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;I'm not sure that I &lt;em&gt;need&lt;/em&gt; to introduce all of the extra machinery
   that you'll find below. But it seems clear that if I don't, I'm left
   with the burden of writing more scaffolding of my own. I'd rather not
   do that, so I'm willing to digest a bit of boilerplate in order to
   avoid make-work.
&lt;/p&gt;

&lt;h2&gt;The lexer&lt;/h2&gt;
&lt;p&gt;We don't want to use Ocaml's own lexer, since Ocaml's notion of a
   valid token is very different from our simple language's. In "echo", a
   token is any sequence of non-whitespace characters. We don't need
   classify our tokens (e.g. into identifiers, numbers, punctuation).
&lt;/p&gt;
&lt;p&gt;Ocaml comes with a tool very similar to the classic &lt;a href="http://en.wikipedia.org/wiki/Lex_programming_tool"&gt;lex&lt;/a&gt; for
   generating lexers. It's called &lt;a href="http://plus.kaist.ac.kr/~shoh/ocaml/ocamllex-ocamlyacc/ocamllex-tutorial/"&gt;Ocamllex&lt;/a&gt;, and &lt;code&gt;camlp5&lt;/code&gt; has support
   for integrating with an Ocammlex lexer. (You don't have to use
   Ocamllex; &lt;code&gt;camlp5&lt;/code&gt; also has a roll-your-own-lexer alternative, but I
   don't see the benefit.) Here's the definition of our simple lexer,
   &lt;code&gt;simplelex.mll&lt;/code&gt;:
&lt;/p&gt;
&lt;div&gt;&lt;div class="source"&gt;&lt;pre&gt;&lt;span style="color: #666666"&gt;{&lt;/span&gt; &lt;span style="color: #008000; font-weight: bold"&gt;open&lt;/span&gt; &lt;span style="color: #0000FF; font-weight: bold"&gt;Lexing&lt;/span&gt; &lt;span style="color: #666666"&gt;}&lt;/span&gt;

&lt;span style="color: #008000; font-weight: bold"&gt;let&lt;/span&gt; nonspace &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #666666"&gt;[^&lt;/span&gt; &lt;span style="color: #BA2121"&gt;' '&lt;/span&gt; &lt;span style="color: #BA2121"&gt;'\t'&lt;/span&gt; &lt;span style="color: #BA2121"&gt;'\n'&lt;/span&gt;&lt;span style="color: #666666"&gt;]+&lt;/span&gt;
&lt;span style="color: #008000; font-weight: bold"&gt;let&lt;/span&gt; otherwise &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #666666"&gt;_&lt;/span&gt;

rule token &lt;span style="color: #666666"&gt;=&lt;/span&gt; parse
              &lt;span style="color: #408080; font-style: italic"&gt;(* non-space sequences are tokens. *)&lt;/span&gt;
    nonspace  &lt;span style="color: #666666"&gt;{&lt;/span&gt; &lt;span style="color: #666666"&gt;(&lt;/span&gt;&lt;span style="color: #BA2121"&gt;"TOK"&lt;/span&gt;&lt;span style="color: #666666"&gt;,&lt;/span&gt; lexeme lexbuf&lt;span style="color: #666666"&gt;)&lt;/span&gt; &lt;span style="color: #666666"&gt;}&lt;/span&gt;
              &lt;span style="color: #408080; font-style: italic"&gt;(* 'eof' is special: always include a rule for it. *)&lt;/span&gt;
  &lt;span style="color: #666666"&gt;|&lt;/span&gt; eof       &lt;span style="color: #666666"&gt;{&lt;/span&gt; &lt;span style="color: #666666"&gt;(&lt;/span&gt;&lt;span style="color: #BA2121"&gt;"EOF"&lt;/span&gt;&lt;span style="color: #666666"&gt;,&lt;/span&gt; lexeme lexbuf&lt;span style="color: #666666"&gt;)&lt;/span&gt; &lt;span style="color: #666666"&gt;}&lt;/span&gt; 
              &lt;span style="color: #408080; font-style: italic"&gt;(* everything else: in this case, whitespace. *)&lt;/span&gt;
  &lt;span style="color: #666666"&gt;|&lt;/span&gt; otherwise &lt;span style="color: #666666"&gt;{&lt;/span&gt; token lexbuf &lt;span style="color: #666666"&gt;}&lt;/span&gt;      &lt;span style="color: #408080; font-style: italic"&gt;(* "ignore" *)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;(This is an &lt;code&gt;.mll&lt;/code&gt; file: Ocamllex will generate the related &lt;code&gt;.ml&lt;/code&gt; file
   for us.) We have to be careful to include the &lt;code&gt;eof&lt;/code&gt; rule, which is
   specified by Ocamllex itself and expected by our parser as and
   end-of-input marker. The ordering of the rules is important: since
   we're using an "anything-else" clause to represent whitespace, it must
   appear last.
&lt;/p&gt;

&lt;h2&gt;Integrating the lexer&lt;/h2&gt;
&lt;p&gt;In the &lt;a href="http://fawcett.blogspot.com/2010/02/making-null-language-with-ocaml.html"&gt;previous article&lt;/a&gt;, our parser was a function that accepted a
   stream of characters as input, which we ignored; the parser was
   expected to translate the stream into some top-level code. I could
   have written "echo" that way, and avoided the need for a formal lexer.
   But there's a drawback to that approach. If you read the stream
   yourself, directly, you're also responsible for reporting the
   &lt;em&gt;locations&lt;/em&gt; of your tokens into the parser. In the null-language, we
   used a "dummy location" to sidestep this requirement; but that's not a
   great strategy for a larger language. Those locations will turn out to
   be very helpful when your language grows an actual syntax, and you
   want to start pinpointing errors in your source code. Today, let's pay
   the boilerplate tax and have &lt;code&gt;camlp5&lt;/code&gt; do the housekeeping for us.
&lt;/p&gt;
&lt;p&gt;We begin our parser, &lt;code&gt;pa_echo.ml&lt;/code&gt;, with the lexer-integration
   boilerplate:
&lt;/p&gt;
&lt;div&gt;&lt;div class="source"&gt;&lt;pre&gt;&lt;span style="color: #008000; font-weight: bold"&gt;open&lt;/span&gt; &lt;span style="color: #0000FF; font-weight: bold"&gt;Pcaml&lt;/span&gt;

&lt;span style="color: #008000; font-weight: bold"&gt;let&lt;/span&gt; replace_lexer lxrule &lt;span style="color: #666666"&gt;=&lt;/span&gt; 
  &lt;span style="color: #008000; font-weight: bold"&gt;let&lt;/span&gt; wrapped_lexer &lt;span style="color: #666666"&gt;=&lt;/span&gt;     &lt;span style="color: #408080; font-style: italic"&gt;(* for details, see http://is.gd/960EQ *)&lt;/span&gt;
    &lt;span style="color: #666666"&gt;{&lt;/span&gt; &lt;span style="color: #0000FF; font-weight: bold"&gt;Plexing&lt;/span&gt;.tok_func  &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #666666"&gt;(&lt;/span&gt;&lt;span style="color: #0000FF; font-weight: bold"&gt;Token&lt;/span&gt;.lexer_func_of_ocamllex lxrule&lt;span style="color: #666666"&gt;);&lt;/span&gt;
      &lt;span style="color: #0000FF; font-weight: bold"&gt;Plexing&lt;/span&gt;.tok_using &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #666666"&gt;(&lt;/span&gt;&lt;span style="color: #008000; font-weight: bold"&gt;fun&lt;/span&gt; &lt;span style="color: #666666"&gt;_&lt;/span&gt; &lt;span style="color: #666666"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color: #008000"&gt;()&lt;/span&gt;&lt;span style="color: #666666"&gt;);&lt;/span&gt;
      tok_removing      &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #666666"&gt;(&lt;/span&gt;&lt;span style="color: #008000; font-weight: bold"&gt;fun&lt;/span&gt; &lt;span style="color: #666666"&gt;_&lt;/span&gt; &lt;span style="color: #666666"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color: #008000"&gt;()&lt;/span&gt;&lt;span style="color: #666666"&gt;);&lt;/span&gt;
      tok_match         &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #0000FF; font-weight: bold"&gt;Plexing&lt;/span&gt;.default_match&lt;span style="color: #666666"&gt;;&lt;/span&gt;
      tok_comm          &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #0000FF; font-weight: bold"&gt;None&lt;/span&gt;&lt;span style="color: #666666"&gt;;&lt;/span&gt;
      tok_text          &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #0000FF; font-weight: bold"&gt;Plexing&lt;/span&gt;.lexer_text &lt;span style="color: #666666"&gt;}&lt;/span&gt;
  &lt;span style="color: #008000; font-weight: bold"&gt;in&lt;/span&gt; 
    &lt;span style="color: #0000FF; font-weight: bold"&gt;Grammar&lt;/span&gt;.&lt;span style="color: #0000FF; font-weight: bold"&gt;Unsafe&lt;/span&gt;.gram_reinit &lt;span style="color: #0000FF; font-weight: bold"&gt;Pcaml&lt;/span&gt;.gram wrapped_lexer

&lt;span style="color: #008000; font-weight: bold"&gt;let&lt;/span&gt; &lt;span style="color: #008000"&gt;()&lt;/span&gt; &lt;span style="color: #666666"&gt;=&lt;/span&gt; replace_lexer &lt;span style="color: #0000FF; font-weight: bold"&gt;Simplelex&lt;/span&gt;.token
&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;The last line is the money shot. Note that &lt;code&gt;Simplelex&lt;/code&gt; is the
   module-name associated with our lexer, defined in &lt;code&gt;simplelex.ml&lt;/code&gt;, and
   &lt;code&gt;token&lt;/code&gt; is the name of the lexing rule we defined there.
&lt;/p&gt;

&lt;h2&gt;EXTEND-ing the Ocaml grammar&lt;/h2&gt;
&lt;p&gt;The easiest way that I could find to get &lt;code&gt;camlp5&lt;/code&gt; to both recognize a
   custom lexer &lt;em&gt;and&lt;/em&gt; let you define your own toplevel parser was to use
   a special syntax called "EXTEND", which provides a pattern-matching
   language for overloading the &lt;code&gt;camlp5&lt;/code&gt; parsers. There's a &lt;code&gt;camlp5&lt;/code&gt;
   plugin called &lt;code&gt;pa_extend&lt;/code&gt; which defines the EXTEND syntax: see the
   Makefile, below.
&lt;/p&gt;
&lt;p&gt;Let's continue writing &lt;code&gt;pa_echo.ml&lt;/code&gt;, and define our parser:
&lt;/p&gt;
&lt;div&gt;&lt;div class="source"&gt;&lt;pre&gt;&lt;span style="color: #008000; font-weight: bold"&gt;let&lt;/span&gt; echo token loc &lt;span style="color: #666666"&gt;=&lt;/span&gt;
  &lt;span style="color: #666666"&gt;(&amp;lt;:&lt;/span&gt;str_item&lt;span style="color: #666666"&gt;&amp;lt;&lt;/span&gt; print_endline &lt;span style="color: #666666"&gt;(&lt;/span&gt;&lt;span style="color: #BA2121"&gt;"EMIT: "&lt;/span&gt; &lt;span style="color: #666666"&gt;^&lt;/span&gt; &lt;span style="color: #666666"&gt;$&lt;/span&gt;str&lt;span style="color: #666666"&gt;:&lt;/span&gt;token&lt;span style="color: #666666"&gt;$)&lt;/span&gt; &lt;span style="color: #666666"&gt;&amp;gt;&amp;gt;,&lt;/span&gt; loc&lt;span style="color: #666666"&gt;);;&lt;/span&gt;

&lt;span style="color: #0000FF; font-weight: bold"&gt;EXTEND&lt;/span&gt;
implem&lt;span style="color: #666666"&gt;:&lt;/span&gt;
&lt;span style="color: #666666"&gt;[&lt;/span&gt; &lt;span style="color: #666666"&gt;[&lt;/span&gt; st &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #0000FF; font-weight: bold"&gt;LIST0&lt;/span&gt; &lt;span style="color: #666666"&gt;[&lt;/span&gt; s &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #0000FF; font-weight: bold"&gt;TOK&lt;/span&gt; &lt;span style="color: #666666"&gt;-&amp;gt;&lt;/span&gt; echo s loc &lt;span style="color: #666666"&gt;];&lt;/span&gt; &lt;span style="color: #0000FF; font-weight: bold"&gt;EOF&lt;/span&gt; &lt;span style="color: #666666"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color: #666666"&gt;(&lt;/span&gt;st&lt;span style="color: #666666"&gt;,&lt;/span&gt; &lt;span style="color: #008000"&gt;false&lt;/span&gt;&lt;span style="color: #666666"&gt;)&lt;/span&gt; &lt;span style="color: #666666"&gt;]&lt;/span&gt; &lt;span style="color: #666666"&gt;];&lt;/span&gt;
&lt;span style="color: #0000FF; font-weight: bold"&gt;END&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;echo&lt;/code&gt; function takes a token and its location from the lexer, and
   returns a pair of values: a structure item (toplevel expression), and
   the location. If you take a look back at the last article, you might
   recognize a similar data structure in the null-parser's definition.
   The &lt;code&gt;&amp;lt;:str_item&amp;lt; . &amp;gt;&amp;gt;&lt;/code&gt; thing is a &lt;a href="http://pauillac.inria.fr/~ddr/camlp5/doc/htmlc/quot.html"&gt;quotation&lt;/a&gt; that builds an AST
   node for our toplevel expression; the &lt;code&gt;$str:token$&lt;/code&gt; bit inside tells
   the quotation-expander to treat the token as a literal string in the
   generated code.
&lt;/p&gt;
&lt;p&gt;In the previous article, we learned that &lt;code&gt;implem&lt;/code&gt; is &lt;code&gt;camlp5&lt;/code&gt;'s
   top-level rule for parsing an Ocaml program. Here we overload &lt;code&gt;implem&lt;/code&gt;
   using the EXTEND syntax. The EXTEND section reads: &lt;code&gt;implem&lt;/code&gt;
   takes a list of zero or more tokens, which are turned into toplevel
   expressions via the &lt;code&gt;echo&lt;/code&gt; function, until the end-of-input is
   encountered. Then, the list of toplevel expressions (and their
   locations in the source) are returned to &lt;code&gt;camlp5&lt;/code&gt; for further
   processing (along with the &lt;code&gt;false&lt;/code&gt; flag which we'll avoid discussing
   today).
&lt;/p&gt;
&lt;p&gt;(Being an astute reader, you probably noticed that &lt;code&gt;loc&lt;/code&gt; is used in
   the definition of &lt;code&gt;implem&lt;/code&gt;, but it's not clear where &lt;code&gt;loc&lt;/code&gt;'s value
   is coming from. Recall that EXTEND is a syntax-extension: the code
   into which the EXTEND statement is expanded will bind a value to the
   &lt;code&gt;loc&lt;/code&gt; identifier.)
&lt;/p&gt;

&lt;h2&gt;Build and test&lt;/h2&gt;
&lt;p&gt;This article isn't about best-practices for &lt;em&gt;building&lt;/em&gt; Ocaml code: you
   have several options there. To make the steps clear, I'm using a
   Makefile with three rules: one to generate our lexer with Ocamllex;
   one to build our echo parser; and one to use "echo" to compile the
   &lt;code&gt;hello.src&lt;/code&gt; program at the top of the article.
&lt;/p&gt;
&lt;div&gt;&lt;div class="source"&gt;&lt;pre&gt;&lt;span style="color: #0000FF"&gt;simplelex.ml&lt;/span&gt;&lt;span style="color: #666666"&gt;:&lt;/span&gt; &lt;span style="color: #666666"&gt;simplelex.mll&lt;/span&gt;
        ocamllex simplelex.mll

&lt;span style="color: #0000FF"&gt;pa_echo.cma&lt;/span&gt;&lt;span style="color: #666666"&gt;:&lt;/span&gt; &lt;span style="color: #666666"&gt;pa_echo.ml simplelex.ml&lt;/span&gt;
        ocamlc -I +camlp5 -pp &lt;span style="color: #BA2121"&gt;'camlp5o pa_extend.cmo q_MLast.cmo'&lt;/span&gt; &lt;span style="color: #BB6622; font-weight: bold"&gt;\&lt;/span&gt;
                -a -o pa_echo.cma &lt;span style="color: #BB6622; font-weight: bold"&gt;\&lt;/span&gt;
                simplelex.ml pa_echo.ml

&lt;span style="color: #0000FF"&gt;hello&lt;/span&gt;&lt;span style="color: #666666"&gt;:&lt;/span&gt; &lt;span style="color: #666666"&gt;hello.src pa_echo.cma&lt;/span&gt;
        ocamlopt -pp &lt;span style="color: #BA2121"&gt;'camlp5 ./pa_echo.cma pr_dump.cmo -impl'&lt;/span&gt; &lt;span style="color: #BB6622; font-weight: bold"&gt;\&lt;/span&gt;
        -impl hello.src -o hello
&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Run &lt;code&gt;make hello&lt;/code&gt; and then &lt;code&gt;./hello&lt;/code&gt; and you should get the expected result.
&lt;/p&gt;

&lt;h2&gt;Seeing the generated code&lt;/h2&gt;
&lt;p&gt;If you want, you can ask &lt;code&gt;camlp5&lt;/code&gt; to show you the Ocaml code it
   generates through our "echo" parser. Try this:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;camlp5 ./pa_echo.cma pr_r.cmo -impl hello.src | sed "s/; /;\n/g"
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note the similarities and differences with the &lt;code&gt;hello&lt;/code&gt; rule in the
   Makefile. The file &lt;code&gt;pr_r.cmo&lt;/code&gt; is the "pretty printer" that outputs the
   Ocaml source code. The &lt;code&gt;sed&lt;/code&gt; pipe is optional, but will add some
   newlines for easier reading. Running this on my &lt;code&gt;hello.src&lt;/code&gt;, I get:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;print_endline ("EMIT: " ^ "This");
print_endline ("EMIT: " ^ "is");
print_endline ("EMIT: " ^ "my");
print_endline ("EMIT: " ^ "hello-world");
print_endline ("EMIT: " ^ "program.");
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&amp;hellip;where &lt;code&gt;^&lt;/code&gt; is Ocaml's string-concatenation operator.  If this were a
   real language, I'd probably put that command into a shell-script,
   e.g., &lt;code&gt;echodump&lt;/code&gt;, and rework the Makefile's &lt;code&gt;hello&lt;/code&gt; rule into a
   compiler-script, e.g. &lt;code&gt;echoc&lt;/code&gt;.
&lt;/p&gt;
&lt;p&gt;In the generated code, I see that we're concatenating the strings at
   runtime. It would be possible to concatenate them at compile-time
   (making the program run a billionth of a second faster) by moving
   the concatenation  to a different spot in the quotation
   expression. Can you figure out how?
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7029981-6941779367600366649?l=fawcett.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fawcett.blogspot.com/feeds/6941779367600366649/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7029981&amp;postID=6941779367600366649' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/6941779367600366649'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/6941779367600366649'/><link rel='alternate' type='text/html' href='http://fawcett.blogspot.com/2010/02/one-upping-null-parser-amazing-echo.html' title='One-upping the null parser: the amazing Echo language'/><author><name>fawcett</name><uri>http://www.blogger.com/profile/03670078611251538949</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7029981.post-90668136155757409</id><published>2010-02-19T17:47:00.002-05:00</published><updated>2010-02-19T18:38:12.898-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ocaml'/><title type='text'>Making a null language with Ocaml</title><content type='html'>&lt;p&gt;I wanted to learn how to use &lt;a href="http://caml.inria.fr/"&gt;Ocaml&lt;/a&gt; to write a &lt;a href="http://en.wikipedia.org/wiki/Domain-specific_language"&gt;little language&lt;/a&gt;
   for a hobby project. Ocaml has an powerful set of language-development
   tools &amp;mdash; from lexer and parser generators to native-code compilers &amp;mdash; but the
   documentation is not always geared toward the novice.  I've found some
   very thorough examples, but most of them are overly worked, making it
   hard to understand how the various parts fit together.
&lt;/p&gt;
&lt;p&gt;Today, I'm going to implement a &lt;em&gt;null parser&lt;/em&gt;: that
   is, a parser that completely ignores its input. Feed it whatever you
   want: a Java program, your grocery list, whatever &amp;mdash; regardless, it
   will always emit the same, static code. Since there's no actual
   parsing going on, what remains ought to be the minimum scaffolding
   required to host a real parser.
&lt;/p&gt;
&lt;p&gt;The input to the parser (which we're going to ignore) is a &lt;code&gt;Stream&lt;/code&gt; of
   characters. The output is an Ocaml &lt;a href="http://en.wikipedia.org/wiki/Abstract_syntax_tree"&gt;abstract syntax tree&lt;/a&gt;, which can
   be pretty-printed for human consumption or fed directly into the Ocaml
   compiler. The compiler takes the AST and generates an executable.
&lt;/p&gt;

&lt;h2&gt;(Sidebar: The murky lineage of camlp5 and camlp4)&lt;/h2&gt;
&lt;p&gt;Skip this paragraph if you're easily confused. Ocaml comes bundled
   with a tool called the "Caml preprocessor and pretty printer", or
   &lt;code&gt;camlp4&lt;/code&gt; for short. The primary task of &lt;code&gt;camlp4&lt;/code&gt; is to parse Ocaml
   source code and emit Ocaml AST into the compiler. To make a long story
   short, &lt;code&gt;camlp4&lt;/code&gt; underwent a significant design change a while
   back; but some people preferred the earlier version, so they forked the
   old &lt;code&gt;camlp4&lt;/code&gt; and packaged it up as an alternative package called
   &lt;code&gt;camlp5&lt;/code&gt;. So, &lt;code&gt;camlp5&lt;/code&gt; is older tech than &lt;code&gt;camlp4&lt;/code&gt;. 
&lt;/p&gt;
&lt;p&gt;I'm using &lt;code&gt;camlp5&lt;/code&gt;
   here, for a number of reasons (including the superior documentation
   available for it: most &lt;code&gt;camlp4&lt;/code&gt; documents online refer to the &lt;em&gt;old&lt;/em&gt;
   &lt;code&gt;camlp4&lt;/code&gt;, not the new one, and so are actually compatible with
   &lt;code&gt;camlp5&lt;/code&gt;. I know, you couldn't make this stuff up.).
&lt;/p&gt;

&lt;h2&gt;The null parser&lt;/h2&gt;
&lt;p&gt;Let's jump into the implementation. Here's the parser, &lt;code&gt;pa_null.ml&lt;/code&gt;:
&lt;/p&gt;
&lt;div&gt;&lt;div class="source"&gt;&lt;pre&gt;&lt;span style="color: #408080; font-style: italic"&gt;(* define the parser *)&lt;/span&gt;
&lt;span style="color: #008000; font-weight: bold"&gt;let&lt;/span&gt; null_parser stream &lt;span style="color: #666666"&gt;=&lt;/span&gt;
  &lt;span style="color: #008000; font-weight: bold"&gt;let&lt;/span&gt; loc &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #0000FF; font-weight: bold"&gt;Ploc&lt;/span&gt;.dummy 
  &lt;span style="color: #AA22FF; font-weight: bold"&gt;and&lt;/span&gt; directive_found &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #008000"&gt;false&lt;/span&gt; 
  &lt;span style="color: #008000; font-weight: bold"&gt;in&lt;/span&gt;
    &lt;span style="color: #666666"&gt;(&lt;/span&gt; &lt;span style="color: #666666"&gt;[&lt;/span&gt; &lt;span style="color: #666666"&gt;(&amp;lt;:&lt;/span&gt;str_item&lt;span style="color: #666666"&gt;&amp;lt;&lt;/span&gt; print_endline &lt;span style="color: #BA2121"&gt;"|your code          |"&lt;/span&gt;&lt;span style="color: #666666"&gt;;&lt;/span&gt; &lt;span style="color: #666666"&gt;&amp;gt;&amp;gt;,&lt;/span&gt; loc&lt;span style="color: #666666"&gt;);&lt;/span&gt;
        &lt;span style="color: #666666"&gt;(&amp;lt;:&lt;/span&gt;str_item&lt;span style="color: #666666"&gt;&amp;lt;&lt;/span&gt; print_endline &lt;span style="color: #BA2121"&gt;"|has been replaced  |"&lt;/span&gt;&lt;span style="color: #666666"&gt;;&lt;/span&gt; &lt;span style="color: #666666"&gt;&amp;gt;&amp;gt;,&lt;/span&gt; loc&lt;span style="color: #666666"&gt;);&lt;/span&gt;
        &lt;span style="color: #666666"&gt;(&amp;lt;:&lt;/span&gt;str_item&lt;span style="color: #666666"&gt;&amp;lt;&lt;/span&gt; print_endline &lt;span style="color: #BA2121"&gt;"|by the null parser.|"&lt;/span&gt;&lt;span style="color: #666666"&gt;;&lt;/span&gt; &lt;span style="color: #666666"&gt;&amp;gt;&amp;gt;,&lt;/span&gt; loc&lt;span style="color: #666666"&gt;);&lt;/span&gt;
      &lt;span style="color: #666666"&gt;],&lt;/span&gt; directive_found&lt;span style="color: #666666"&gt;)&lt;/span&gt;

&lt;span style="color: #408080; font-style: italic"&gt;(* install it *)&lt;/span&gt;
&lt;span style="color: #008000; font-weight: bold"&gt;let&lt;/span&gt; &lt;span style="color: #008000"&gt;()&lt;/span&gt; &lt;span style="color: #666666"&gt;=&lt;/span&gt; &lt;span style="color: #0000FF; font-weight: bold"&gt;Pcaml&lt;/span&gt;.parse_implem &lt;span style="color: #666666"&gt;:=&lt;/span&gt; null_parser
&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;We're providing an alternate implementation for &lt;code&gt;Pcaml.parse_implem&lt;/code&gt;,
   which is &lt;code&gt;camlp5&lt;/code&gt;'s toplevel parsing rule: it's the maestro that reads
   the source code, and calls upon a suite of smaller parsers to break
   the code down into an AST. &lt;code&gt;Pcaml.parse_implem&lt;/code&gt;'s output is a pair of
   values: a list of &lt;em&gt;structure items&lt;/em&gt; (toplevel expressions, of type
   &lt;code&gt;str_item&lt;/code&gt;) and their corresponding locations in the source code; and
   a boolean flag (&lt;code&gt;directive_found&lt;/code&gt;) which we're totally going to
   ignore.
&lt;/p&gt;
&lt;p&gt;Since we're not actually lexing any code, there aren't any source-code
   locations to speak of; so we use &lt;code&gt;Ploc.dummy&lt;/code&gt; to create a
   dummy-location, just to keep &lt;code&gt;camlp5&lt;/code&gt; happy. (In a real parser, these
   locations could be used to indicate the exact locations of errors in
   the source code.)
&lt;/p&gt;
&lt;p&gt;The toplevel expressions (structure items) are expressed using a
   &lt;a href="http://pauillac.inria.fr/~ddr/camlp5/doc/htmlc/quot.html"&gt;quotation syntax&lt;/a&gt; which expands the quoted expression into an AST
   node. (Strictly, these quotations are optional: our null-parser could
   have emitted a program with no instructions in it. But it's bad enough that our parser reads nothing: it ought to at least spit something out.) So,
&lt;/p&gt;
&lt;div&gt;&lt;div class="source"&gt;&lt;pre&gt;&lt;span style="color: #666666"&gt;&amp;lt;:&lt;/span&gt;str_item&lt;span style="color: #666666"&gt;&amp;lt;&lt;/span&gt; print_endline &lt;span style="color: #BA2121"&gt;"|by the null parser.|"&lt;/span&gt;&lt;span style="color: #666666"&gt;;&lt;/span&gt; &lt;span style="color: #666666"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;hellip;is expanded  into an AST node  for a toplevel  expression, which in
   this case  is an instruction to print  a message to the  screen. (In a
   Lisp language, this would be  the 'backtick' in a macro definition. In
   Ocaml's case,  the syntax expressions are statically typed, hence the
   &lt;code&gt;:str_item&lt;/code&gt; declaration.)
&lt;/p&gt;
&lt;p&gt;Note that while the &lt;code&gt;stream&lt;/code&gt; argument is passed into our parser, we
   never refer to it. The original &lt;code&gt;Pcaml.parse_implem&lt;/code&gt; rule (which we
   replaced) would have lexed the stream into Ocaml-syntax tokens, and
   then parsed those tokens into an AST.
&lt;/p&gt;

&lt;h2&gt;Building and using the parser&lt;/h2&gt;
&lt;p&gt;We'll use a Makefile to manage our building. The first rule
   builds the parser, and the second rule uses it to compile a hello-world
   program.
&lt;/p&gt;
&lt;div&gt;&lt;div class="source"&gt;&lt;pre&gt;&lt;span style="color: #0000FF"&gt;pa_null.cmo&lt;/span&gt;&lt;span style="color: #666666"&gt;:&lt;/span&gt; &lt;span style="color: #666666"&gt;pa_null.ml&lt;/span&gt;
    ocamlc -I +camlp5 -pp &lt;span style="color: #BA2121"&gt;'camlp5o q_ast.cmo'&lt;/span&gt; -c pa_null.ml

&lt;span style="color: #0000FF"&gt;hello&lt;/span&gt;&lt;span style="color: #666666"&gt;:&lt;/span&gt; &lt;span style="color: #666666"&gt;hello.ml pa_null.cmo&lt;/span&gt;
    ocamlc -pp &lt;span style="color: #BA2121"&gt;'camlp5o ./pa_null.cmo'&lt;/span&gt; hello.ml -o hello
&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;-pp&lt;/code&gt; statements are where we invoke &lt;code&gt;camlp5&lt;/code&gt; as a preprocessor:
   first, using the &lt;code&gt;q_ast&lt;/code&gt; (quotations) module to enable the quotations-syntax we used in our parser; and second, using our own &lt;code&gt;pa_null&lt;/code&gt;
   parser to "parse" our hello-world program.
&lt;/p&gt;
&lt;p&gt;Put whatever you want in &lt;code&gt;hello.ml&lt;/code&gt;, and run &lt;code&gt;make hello&lt;/code&gt;. The
   resulting executable, &lt;code&gt;hello&lt;/code&gt;, should produce the following output:
&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ./hello
|your code          |
|has been replaced  |
|by the null parser.|
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That's that.
&lt;/p&gt;
&lt;p&gt;While this isn't an introduction to Ocaml, if you want to follow along
   at home, both Ocaml and the &lt;code&gt;camlp5&lt;/code&gt; processor are available in
   Debian/Ubuntu's package systems (and are probably available in Fink or
   MacPorts for you Mac users).
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7029981-90668136155757409?l=fawcett.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fawcett.blogspot.com/feeds/90668136155757409/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7029981&amp;postID=90668136155757409' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/90668136155757409'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/90668136155757409'/><link rel='alternate' type='text/html' href='http://fawcett.blogspot.com/2010/02/making-null-language-with-ocaml.html' title='Making a null language with Ocaml'/><author><name>fawcett</name><uri>http://www.blogger.com/profile/03670078611251538949</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7029981.post-8331035054686608123</id><published>2008-02-15T12:54:00.003-05:00</published><updated>2008-02-15T12:58:48.097-05:00</updated><title type='text'>PostgreSQL Love: deleting from views</title><content type='html'>&lt;p&gt;I know that MySQL gets most of the attention in the relational-database field, as an easy-to-install, general-purpose relational database. But I've used PostgreSQL for years, and sometimes I have a moment like I did today that reminds me why.
&lt;/p&gt;&lt;p&gt;In a quizzing application I run, I have an  'attempts' table with a lot of columns, and many of them are very wide. So, I created a view of the 'attempts' table that contains the key columns, and most of the time this view is what I use when I'm looking something up.
&lt;/p&gt;&lt;p&gt;The problem is, you cannot delete records from a view (and rightly so). So, I often end up with a scenario like this:
&lt;/p&gt;&lt;pre&gt;# select * from attempts_brief where quiz=1479;
id    | quiz | person | score | out_of
-------+------+--------+-------+----
14981 | 1479 | admin  |    20 |     20
14982 | 1479 | admin  |       |  
(2 rows)&lt;/pre&gt;I want to delete those two dummy rows, so I alter the query, changing &lt;code&gt;SELECT *&lt;/code&gt; to &lt;code&gt;DELETE&lt;/code&gt;, as I would on any other cautious deletion attempt. But I get this:

&lt;pre&gt;# delete from attempts_brief where quiz=1479;
ERROR:  cannot delete from a view
HINT:  You need an unconditional ON DELETE DO INSTEAD rule.&lt;/pre&gt;Of course, the deletion fails, and I'm reminded that I'm not querying a table directly: silly programmer. But that hint, what a beaut! Using &lt;a href="http://www.postgresql.org/docs/8.1/static/rules-update.html"&gt;PostgreSQL's Rules system&lt;/a&gt;, I can tell the database what I really mean to do:
&lt;pre&gt;# create rule attempts_brief_del as on delete to attempts_brief
# do instead delete from attempts where id=OLD.id;
CREATE RULE&lt;/pre&gt;&lt;p&gt;Now that the rule is in place, I can delete from the view, and the database will do the Right Thing:
&lt;/p&gt;&lt;pre&gt;# delete from attempts_brief where quiz=1479;
DELETE 2&lt;/pre&gt;&lt;p&gt;Perfect!
&lt;/p&gt;&lt;p&gt;It's not a huge thing, I know. But it just reminds me how much time the Postgres designers spent, not only creating a system that is fast, consistent and powerful, but that is helpful, flexible and user-friendly to boot. I love ya, Postgres.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7029981-8331035054686608123?l=fawcett.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.postgresql.org/docs/8.1/static/rules-update.html' title='PostgreSQL Love: deleting from views'/><link rel='replies' type='application/atom+xml' href='http://fawcett.blogspot.com/feeds/8331035054686608123/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7029981&amp;postID=8331035054686608123' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/8331035054686608123'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/8331035054686608123'/><link rel='alternate' type='text/html' href='http://fawcett.blogspot.com/2008/02/postgresql-love-deleting-from-views.html' title='PostgreSQL Love: deleting from views'/><author><name>fawcett</name><uri>http://www.blogger.com/profile/03670078611251538949</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7029981.post-6110825191765039971</id><published>2007-08-16T16:25:00.000-04:00</published><updated>2007-08-16T16:44:56.478-04:00</updated><title type='text'>Roman Numerals in Haskell</title><content type='html'>Here's a toy Haskell program, for converting integers to Roman numerals. It's inspired directly by &lt;a href="http://code-factor.blogspot.com/2007/06/roman-numeral-conversion-in-factor.html"&gt;Doug Coleman's Factor version&lt;/a&gt;; I'm using the same algorithm he describes.&lt;p&gt;(Haskell's an interesting language for writing little things like this. I haven't used a statically-typed language in a long time; it's kind of like taking a hike in someone else's boots. The &lt;a href="http://haskell.org/haskellwiki/Monad"&gt;monad thing&lt;/a&gt; hasn't been as hard to wrap my head around as I initially thought it might be; I particularly like the way that exception handling can be generalized using monads.)

&lt;pre&gt;&lt;span class="comment-delimiter"&gt;-- &lt;/span&gt;&lt;span class="comment"&gt;inspired by
&lt;/span&gt;&lt;span class="comment-delimiter"&gt;-- &lt;/span&gt;&lt;span class="comment"&gt;http://code-factor.blogspot.com/2007/06/roman-numeral-conversion-in-factor.html
&lt;/span&gt;
&lt;span class="keyword"&gt;module&lt;/span&gt; &lt;span class="type"&gt;RomanNumerals&lt;/span&gt; (toRoman) &lt;span class="keyword"&gt;where&lt;/span&gt;

&lt;span class="comment-delimiter"&gt;-- &lt;/span&gt;&lt;span class="comment"&gt;constants
&lt;/span&gt;
&lt;span class="function-name"&gt;romanValues&lt;/span&gt; &lt;span class="variable-name"&gt;=&lt;/span&gt; [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]
&lt;span class="function-name"&gt;romanDigits&lt;/span&gt; &lt;span class="variable-name"&gt;=&lt;/span&gt; words &lt;span class="string"&gt;"m cm d cd c xc l xl x ix v iv i"&lt;/span&gt;
&lt;span class="function-name"&gt;theRomans&lt;/span&gt;   &lt;span class="variable-name"&gt;=&lt;/span&gt; zip romanValues romanDigits


&lt;span class="comment-delimiter"&gt;-- &lt;/span&gt;&lt;span class="comment"&gt;internals
&lt;/span&gt;
&lt;span class="comment-delimiter"&gt;-- &lt;/span&gt;&lt;span class="comment"&gt;To do the actual conversion to Roman numerals, we left-fold over
&lt;/span&gt;&lt;span class="comment-delimiter"&gt;-- &lt;/span&gt;&lt;span class="comment"&gt;theRomans, starting with a seed value of (accumulator,
&lt;/span&gt;&lt;span class="comment-delimiter"&gt;-- &lt;/span&gt;&lt;span class="comment"&gt;remainder). The initial remainder is the value we want to
&lt;/span&gt;&lt;span class="comment-delimiter"&gt;-- &lt;/span&gt;&lt;span class="comment"&gt;convert. Using theRomans as a resource, romFold breaks down the
&lt;/span&gt;&lt;span class="comment-delimiter"&gt;-- &lt;/span&gt;&lt;span class="comment"&gt;remainder by modular arithmetic, adding Roman digits to the
&lt;/span&gt;&lt;span class="comment-delimiter"&gt;-- &lt;/span&gt;&lt;span class="comment"&gt;accumulator as it goes. Finally we take the accumulator, and we're
&lt;/span&gt;&lt;span class="comment-delimiter"&gt;-- &lt;/span&gt;&lt;span class="comment"&gt;done.
&lt;/span&gt;
&lt;span class="function-name"&gt;toRoman'&lt;/span&gt; &lt;span class="variable-name"&gt;::&lt;/span&gt; &lt;span class="type"&gt;Int&lt;/span&gt; &lt;span class="variable-name"&gt;-&gt;&lt;/span&gt; &lt;span class="type"&gt;String&lt;/span&gt;
&lt;span class="function-name"&gt;romFold&lt;/span&gt;  &lt;span class="variable-name"&gt;::&lt;/span&gt; ([a], &lt;span class="type"&gt;Int&lt;/span&gt;) &lt;span class="variable-name"&gt;-&gt;&lt;/span&gt; (&lt;span class="type"&gt;Int&lt;/span&gt;, [a]) &lt;span class="variable-name"&gt;-&gt;&lt;/span&gt; ([a], &lt;span class="type"&gt;Int&lt;/span&gt;)

&lt;span class="function-name"&gt;toRoman'&lt;/span&gt; n &lt;span class="variable-name"&gt;=&lt;/span&gt; fst &lt;span class="variable-name"&gt;$&lt;/span&gt; foldl romFold (&lt;span class="string"&gt;""&lt;/span&gt;, n) theRomans

&lt;span class="function-name"&gt;romFold&lt;/span&gt; (acc, n) (romValue, romDigit) &lt;span class="variable-name"&gt;=&lt;/span&gt;
&lt;span class="keyword"&gt;let&lt;/span&gt; (result, remain) &lt;span class="variable-name"&gt;=&lt;/span&gt; divMod n romValue
    newDigits &lt;span class="variable-name"&gt;=&lt;/span&gt; concat &lt;span class="variable-name"&gt;$&lt;/span&gt; replicate result romDigit
&lt;span class="keyword"&gt;in&lt;/span&gt; (acc &lt;span class="variable-name"&gt;++&lt;/span&gt; newDigits, remain)


&lt;span class="function-name"&gt;romanRangeCheck&lt;/span&gt; &lt;span class="variable-name"&gt;::&lt;/span&gt; (&lt;span class="type"&gt;Monad&lt;/span&gt; m) &lt;span class="variable-name"&gt;=&gt;&lt;/span&gt; &lt;span class="type"&gt;Int&lt;/span&gt; &lt;span class="variable-name"&gt;-&gt;&lt;/span&gt; m &lt;span class="type"&gt;Int&lt;/span&gt;
&lt;span class="function-name"&gt;romanRangeCheck&lt;/span&gt; n
&lt;span class="variable-name"&gt;|&lt;/span&gt; 0 &lt;span class="variable-name"&gt;&lt;&lt;/span&gt; n &lt;span class="variable-name"&gt;&amp;&amp;amp;&lt;/span&gt; n &lt;span class="variable-name"&gt;&lt;&lt;/span&gt; 4000 &lt;span class="variable-name"&gt;=&lt;/span&gt; return n
&lt;span class="variable-name"&gt;|&lt;/span&gt; otherwise &lt;span class="variable-name"&gt;=&lt;/span&gt; fail &lt;span class="string"&gt;"out of bounds [0 &amp;lt; n &amp;lt; 4000]"&lt;/span&gt;


&lt;span class="comment-delimiter"&gt;-- &lt;/span&gt;&lt;span class="comment"&gt;The public, range-checking version.
&lt;/span&gt;
&lt;span class="function-name"&gt;toRoman&lt;/span&gt; &lt;span class="variable-name"&gt;::&lt;/span&gt; &lt;span class="type"&gt;Monad&lt;/span&gt; m &lt;span class="variable-name"&gt;=&gt;&lt;/span&gt; &lt;span class="type"&gt;Int&lt;/span&gt; &lt;span class="variable-name"&gt;-&gt;&lt;/span&gt; m &lt;span class="type"&gt;String&lt;/span&gt;
&lt;span class="function-name"&gt;toRoman&lt;/span&gt; n &lt;span class="variable-name"&gt;=&lt;/span&gt; romanRangeCheck n &lt;span class="variable-name"&gt;&gt;&gt;=&lt;/span&gt; (return &lt;span class="variable-name"&gt;.&lt;/span&gt; toRoman')
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7029981-6110825191765039971?l=fawcett.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://code-factor.blogspot.com/2007/06/roman-numeral-conversion-in-factor.html' title='Roman Numerals in Haskell'/><link rel='replies' type='application/atom+xml' href='http://fawcett.blogspot.com/feeds/6110825191765039971/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7029981&amp;postID=6110825191765039971' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/6110825191765039971'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/6110825191765039971'/><link rel='alternate' type='text/html' href='http://fawcett.blogspot.com/2007/08/roman-numerals-in-haskell.html' title='Roman Numerals in Haskell'/><author><name>fawcett</name><uri>http://www.blogger.com/profile/03670078611251538949</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7029981.post-668079570516565282</id><published>2007-05-02T22:53:00.000-04:00</published><updated>2007-05-02T23:07:41.128-04:00</updated><title type='text'>The kindness of dictators, and an octet-full of eggs</title><content type='html'>&lt;p&gt;&lt;a style="margin: 0pt 1em 1em; float: right;" href="http://www.flickr.com/photos/36076712@N00/481902762/" title="Photo Sharing"&gt;&lt;img src="http://farm1.static.flickr.com/186/481902762_7a66d021be_m.jpg" alt="chicken-shirt" height="240" width="161" /&gt;&lt;/a&gt;
I had a real treat today; I received a t-shirt from Felix Winkelmann, the benevolent dictator of the  &lt;a href="http://www.call-with-current-continuation.org/"&gt;Chicken Scheme&lt;/a&gt; project. My prize was  for submitting the 256&lt;sup&gt;th&lt;/sup&gt; “egg” (extension module) to the language, a &lt;a href="http://chicken.wiki.br/sqlora"&gt;simple library&lt;/a&gt; for accessing Oracle databases. (Of course, it took &lt;a href="http://chicken.wiki.br/Eggs%20Unlimited"&gt;255 contributions&lt;/a&gt;  from others to make mine shirt-worthy: the giants, and their shoulders, did the real work.)   &lt;/p&gt;&lt;p&gt;I've been reflecting upon Scheme recently, not only because of the shirt (which, by the way, pleases me more than an article of clothing ought to), but also because I've been using Scheme in earnest these past few months, building a small-but-serious set of Web projects at work. Before working with Scheme, I would have chosen Python for these projects, without a second thought.  A dynamic language is the only sensible tool for the bob-and-weave, roll-with-the-punches development style that gets projects like these off of the napkin and into production in short time.&lt;/p&gt;  &lt;p&gt; Scheme fills the same dynamic niche, but makes different tradeoffs than Python, and its cousin Ruby, do. It really does feel more like a language-construction kit than a language in itself. You can use Scheme without defining any new syntax, but I think that syntactic freedom is where one of the sweet-spots is. I've done my share of metaprogramming in Python, trying to push the syntax-envelope in order to write concise and representative solutions. In Scheme, there really isn't an envelope to push: you can write anything you want, any way you want it, and sure enough, there's a way to treat it as valid Scheme code.  Problems have a way of growing more complicated over time; the lack of a syntax barrier carries the promise (the siren song?) of  solutions that can evolve in step with their problems. &lt;/p&gt;  &lt;p&gt;Scheme communities are much smaller than those of mainstream languages, so you end up writing a lot of stuff that you take for granted in other languages. That's a strength, of sorts: the devil you wrote is sometimes easier to understand, and often much simpler, than the general library that someone else did. (On the other hand, a programmer who uses his own libraries may have a fool for a client.) The fragmentation of communities doesn't help in the library department; although Scheme-the-language is &lt;a href="http://www.schemers.org/Documents/Standards/"&gt;standardized&lt;/a&gt;, there are several dialects, each not fully compatible with the others. (My Oracle library, for example, is a problem that is already (partially) solved in &lt;a href="http://www.plt-scheme.org/"&gt;PLT Scheme&lt;/a&gt;, but in a PLT-specific manner.) Fragmentation aside, those communities are smart as hell, and if you ask nicely they can help a great deal. &lt;/p&gt;  &lt;p&gt;Anyway, back to those Web projects of mine. I can't say that they any shorter than they would have been in Python, and probably not much more readable. At least half of what I've written has been reusable library code, which is good; but I still write Scheme like a greenhorn, and I can see many areas I'd like to fix. So, it's comparable in complexity to the Python I would have written — though in different ways — but I'm optimistic that it will mature more naturally. I also like the performance; Web apps don't always need a lot of computational power, but it's hard to ignore just how well these apps perform: the hotspots just aren't that hot. &lt;/p&gt;  &lt;p&gt;In short, I earned a shirt while conspiring with giants, in a wild-west, open-source frontier (or backwoods?), using a shockingly-powerful language that my grandpa would have recognized. Man, I love this stuff. &lt;/p&gt;  &lt;hr style="margin-top: 5em;"&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7029981-668079570516565282?l=fawcett.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fawcett.blogspot.com/feeds/668079570516565282/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7029981&amp;postID=668079570516565282' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/668079570516565282'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/668079570516565282'/><link rel='alternate' type='text/html' href='http://fawcett.blogspot.com/2007/05/kindness-of-dictators-and-octet-full-of.html' title='The kindness of dictators, and an octet-full of eggs'/><author><name>fawcett</name><uri>http://www.blogger.com/profile/03670078611251538949</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm1.static.flickr.com/186/481902762_7a66d021be_t.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7029981.post-2014476108243815209</id><published>2007-02-16T15:59:00.000-05:00</published><updated>2007-02-16T15:59:01.841-05:00</updated><title type='text'>Rethinking OpenURL</title><content type='html'>Dan Chudnov says a lot of great things in &lt;a href="http://onebiglibrary.net/story/rethinking-openurl"&gt;Rethinking OpenURL&lt;/a&gt;, but this caught my eye:
&lt;blockquote&gt;"This, my friends, is dynamic service linking, in a nutshell. (And, yes, I do mean 'in a nutshell' as in 'ooh, ooh, help, I'm trapped in a nutshell, get me out of here.')"&lt;/blockquote&gt;And now, I'm off to read the rest before the weekend starts. :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7029981-2014476108243815209?l=fawcett.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://onebiglibrary.net/story/rethinking-openurl' title='Rethinking OpenURL'/><link rel='replies' type='application/atom+xml' href='http://fawcett.blogspot.com/feeds/2014476108243815209/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7029981&amp;postID=2014476108243815209' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/2014476108243815209'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/2014476108243815209'/><link rel='alternate' type='text/html' href='http://fawcett.blogspot.com/2007/02/rethinking-openurl.html' title='Rethinking OpenURL'/><author><name>fawcett</name><uri>http://www.blogger.com/profile/03670078611251538949</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7029981.post-7915046333440227110</id><published>2007-02-16T09:25:00.000-05:00</published><updated>2007-02-16T09:25:14.173-05:00</updated><title type='text'>Hello planet code4lib!</title><content type='html'>Hello to the readers of &lt;a href="http://planet.code4lib.org/"&gt;planet code4lib&lt;/a&gt;! I just found out my blog is being syndicated [t]here.

In future, I'll try to keep the noise down, and finish my snacks before entering the blog...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7029981-7915046333440227110?l=fawcett.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://planet.code4lib.org/' title='Hello planet code4lib!'/><link rel='replies' type='application/atom+xml' href='http://fawcett.blogspot.com/feeds/7915046333440227110/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7029981&amp;postID=7915046333440227110' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/7915046333440227110'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/7915046333440227110'/><link rel='alternate' type='text/html' href='http://fawcett.blogspot.com/2007/02/hello-planet-code4lib.html' title='Hello planet code4lib!'/><author><name>fawcett</name><uri>http://www.blogger.com/profile/03670078611251538949</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7029981.post-5228732045368312632</id><published>2007-02-15T19:18:00.000-05:00</published><updated>2007-02-15T20:01:41.443-05:00</updated><title type='text'>group-by: like itertools.group in Scheme</title><content type='html'>Just a little post. I wanted to share a Scheme procedure that worked something like Python's &lt;a href="http://docs.python.org/lib/itertools-functions.html"&gt;&lt;code&gt;itertools.groupby&lt;/code&gt;&lt;/a&gt; function. Emphasis on "something like": for one thing, it works on lists only, rather than any "iterable" type, which is part of the magic and appeal of &lt;code&gt;itertools&lt;/code&gt;.
&lt;pre&gt;
&lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;use &lt;a href="http://practical-scheme.net/wiliki/schemexref.cgi?SRFI-1"&gt;srfi-1&lt;/a&gt; &lt;a href="http://practical-scheme.net/wiliki/schemexref.cgi?SRFI-26"&gt;srfi-26&lt;/a&gt;&lt;span class="cparen-normal-paren"&gt;)&lt;/span&gt;

&lt;span class="cparen-around-define"&gt;(&lt;/span&gt;&lt;span class="quack-pltish-keyword"&gt;define&lt;/span&gt; &lt;span class="cparen-binding-list"&gt;(&lt;/span&gt;&lt;span class="quack-pltish-defn"&gt;group-by&lt;/span&gt; keyproc lst #!optional &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;is-equal &lt;a href="http://practical-scheme.net/wiliki/schemexref.cgi?equal?"&gt;equal?&lt;/a&gt;&lt;span class="cparen-normal-paren"&gt;)&lt;/span&gt;&lt;span class="cparen-binding-list"&gt;)&lt;/span&gt;
&lt;span class="quack-pltish-comment"&gt;  ;; examples:
&lt;/span&gt;  &lt;span class="quack-pltish-comment"&gt;;; (group-by identity '(1 2 3 3 4 4 4)) --&amp;gt; ((1) (2) (3 3) (4 4 4))                                         
&lt;/span&gt;  &lt;span class="quack-pltish-comment"&gt;;; (group-by &lt;a href="http://practical-scheme.net/wiliki/schemexref.cgi?car"&gt;car&lt;/a&gt; '((a 1) (a 2) (b 1))) --&amp;gt; '(((a 1) (a 2)) ((b 1)))                                                   
&lt;/span&gt;  &lt;span class="cparen-around-letdo"&gt;(&lt;/span&gt;&lt;span class="quack-pltish-keyword"&gt;let&lt;/span&gt; loop &lt;span class="cparen-binding-list"&gt;(&lt;/span&gt;&lt;span class="cparen-binding"&gt;(&lt;/span&gt;lst lst&lt;span class="cparen-binding"&gt;)&lt;/span&gt; &lt;span class="cparen-binding"&gt;(&lt;/span&gt;acc '&lt;span class="cparen-around-quote"&gt;()&lt;/span&gt;&lt;span class="cparen-binding"&gt;)&lt;/span&gt;&lt;span class="cparen-binding-list"&gt;)&lt;/span&gt;
   &lt;span class="cparen-around-conditional"&gt;(&lt;/span&gt;&lt;span class="quack-pltish-keyword"&gt;if&lt;/span&gt; &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;&lt;a href="http://practical-scheme.net/wiliki/schemexref.cgi?null?"&gt;null?&lt;/a&gt; lst&lt;span class="cparen-normal-paren"&gt;)&lt;/span&gt;
       &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;&lt;a href="http://practical-scheme.net/wiliki/schemexref.cgi?reverse"&gt;reverse&lt;/a&gt; acc&lt;span class="cparen-normal-paren"&gt;)&lt;/span&gt;
       &lt;span class="cparen-around-letdo"&gt;(&lt;/span&gt;&lt;span class="quack-pltish-keyword"&gt;let&lt;/span&gt; &lt;span class="cparen-binding-list"&gt;(&lt;/span&gt;&lt;span class="cparen-binding"&gt;(&lt;/span&gt;key &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;keyproc &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;car lst&lt;span class="cparen-normal-paren"&gt;))&lt;/span&gt;&lt;span class="cparen-binding"&gt;)&lt;/span&gt;&lt;span class="cparen-binding-list"&gt;)&lt;/span&gt;
         &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;&lt;span class="quack-pltish-keyword"&gt;&lt;a href="http://practical-scheme.net/wiliki/schemexref.cgi?receive"&gt;receive&lt;/a&gt;&lt;/span&gt; &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;grouped rest&lt;span class="cparen-normal-paren"&gt;)&lt;/span&gt;
             &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;&lt;a href="http://practical-scheme.net/wiliki/schemexref.cgi?span"&gt;span&lt;/a&gt; &lt;span class="cparen-around-lambda"&gt;(&lt;/span&gt;&lt;span class="quack-pltish-keyword"&gt;lambda&lt;/span&gt; &lt;span class="cparen-binding-list"&gt;(&lt;/span&gt;item&lt;span class="cparen-binding-list"&gt;)&lt;/span&gt; &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;is-equal key &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;keyproc item&lt;span class="cparen-normal-paren"&gt;))&lt;/span&gt;&lt;span class="cparen-around-lambda"&gt;)&lt;/span&gt; lst&lt;span class="cparen-normal-paren"&gt;)&lt;/span&gt;
           &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;loop rest &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;&lt;a href="http://practical-scheme.net/wiliki/schemexref.cgi?cons"&gt;cons&lt;/a&gt; grouped acc&lt;span class="cparen-normal-paren"&gt;)))&lt;/span&gt;&lt;span class="cparen-around-letdo"&gt;)&lt;/span&gt;&lt;span class="cparen-around-conditional"&gt;)&lt;/span&gt;&lt;span class="cparen-around-letdo"&gt;)&lt;/span&gt;&lt;span class="cparen-around-define"&gt;)&lt;/span&gt;
&lt;/pre&gt;Also unlike the Python version, there's no default "key" function (Python uses an identity function as the default). To get the Pythonic behaviour, use &lt;code&gt;identity&lt;/code&gt; (or &lt;a href="http://practical-scheme.net/wiliki/schemexref.cgi?values"&gt;values&lt;/a&gt; if your Scheme doesn't have an identity procedure).

The optional is-equal? function lets you specify a different key-comparison function; by default, the comparison function is Scheme's &lt;a href="http://practical-scheme.net/wiliki/schemexref.cgi?equal?"&gt;equal?&lt;/a&gt; which is pretty liberal: strings are compared by value, for example, whereas Scheme's &lt;a href="http://practical-scheme.net/wiliki/schemexref.cgi?eq?"&gt;eq?&lt;/a&gt; compares by identity only. You might want to use a custom comparison function anyway --- for example, to group by strings, but case-insensitively:
&lt;pre&gt;
&lt;span class="comint-highlight-input"&gt;(group-by &lt;a href="http://practical-scheme.net/wiliki/schemexref.cgi?first"&gt;first&lt;/a&gt; '((&lt;/span&gt;&lt;span class="quack-pltish-selfeval"&gt;"fred"&lt;/span&gt;&lt;span class="comint-highlight-input"&gt; 25) (&lt;/span&gt;&lt;span class="quack-pltish-selfeval"&gt;"Fred"&lt;/span&gt;&lt;span class="comint-highlight-input"&gt; 33) (&lt;/span&gt;&lt;span class="quack-pltish-selfeval"&gt;"mary"&lt;/span&gt;&lt;span class="comint-highlight-input"&gt; 22)))&lt;/span&gt;
--&gt; &lt;span class="cparen-normal-paren"&gt;(((&lt;/span&gt;&lt;span class="quack-pltish-selfeval"&gt;"fred"&lt;/span&gt; &lt;span class="quack-pltish-selfeval"&gt;25&lt;/span&gt;&lt;span class="cparen-normal-paren"&gt;))&lt;/span&gt; &lt;span class="cparen-normal-paren"&gt;((&lt;/span&gt;&lt;span class="quack-pltish-selfeval"&gt;"Fred"&lt;/span&gt; &lt;span class="quack-pltish-selfeval"&gt;33&lt;/span&gt;&lt;span class="cparen-normal-paren"&gt;))&lt;/span&gt; &lt;span class="cparen-normal-paren"&gt;((&lt;/span&gt;&lt;span class="quack-pltish-selfeval"&gt;"mary"&lt;/span&gt; &lt;span class="quack-pltish-selfeval"&gt;22&lt;/span&gt;&lt;span class="cparen-normal-paren"&gt;)))&lt;/span&gt;
&lt;span class="comint-highlight-input"&gt;(group-by first '((&lt;/span&gt;&lt;span class="quack-pltish-selfeval"&gt;"fred"&lt;/span&gt;&lt;span class="comint-highlight-input"&gt; 25) (&lt;/span&gt;&lt;span class="quack-pltish-selfeval"&gt;"Fred"&lt;/span&gt;&lt;span class="comint-highlight-input"&gt; 33) (&lt;/span&gt;&lt;span class="quack-pltish-selfeval"&gt;"mary"&lt;/span&gt;&lt;span class="comint-highlight-input"&gt; 22)) &lt;a href="http://practical-scheme.net/wiliki/schemexref.cgi?string-ci=?"&gt;string-ci=?&lt;/a&gt;)&lt;/span&gt;
--&gt; &lt;span class="cparen-normal-paren"&gt;(((&lt;/span&gt;&lt;span class="quack-pltish-selfeval"&gt;"fred"&lt;/span&gt; &lt;span class="quack-pltish-selfeval"&gt;25&lt;/span&gt;&lt;span class="cparen-normal-paren"&gt;)&lt;/span&gt; &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;&lt;span class="quack-pltish-selfeval"&gt;"Fred"&lt;/span&gt; &lt;span class="quack-pltish-selfeval"&gt;33&lt;/span&gt;&lt;span class="cparen-normal-paren"&gt;))&lt;/span&gt; &lt;span class="cparen-normal-paren"&gt;((&lt;/span&gt;&lt;span class="quack-pltish-selfeval"&gt;"mary"&lt;/span&gt; &lt;span class="quack-pltish-selfeval"&gt;22&lt;/span&gt;&lt;span class="cparen-normal-paren"&gt;)))&lt;/span&gt;&lt;/pre&gt;That's all.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7029981-5228732045368312632?l=fawcett.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fawcett.blogspot.com/feeds/5228732045368312632/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7029981&amp;postID=5228732045368312632' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/5228732045368312632'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/5228732045368312632'/><link rel='alternate' type='text/html' href='http://fawcett.blogspot.com/2007/02/group-by-like-itertoolsgroup-in-scheme.html' title='group-by: like itertools.group in Scheme'/><author><name>fawcett</name><uri>http://www.blogger.com/profile/03670078611251538949</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7029981.post-116672684299420916</id><published>2006-12-21T13:47:00.000-05:00</published><updated>2006-12-21T13:47:23.103-05:00</updated><title type='text'>hart 0.1 (or, Man lays egg, news at 11)</title><content type='html'>Happy day — I just submitted &lt;a href="http://www.call-with-current-continuation.org/eggs/hart.html"&gt;Hart&lt;/a&gt;, my very first &lt;a href="http://www.call-with-current-continuation.org/eggs/"&gt;egg&lt;/a&gt; (module) for  &lt;a href="http://www.call-with-current-continuation.org/"&gt;Chicken Scheme&lt;/a&gt;. I feel like a proud papa! It's still an early release, and has lots of room to grow, but I've enjoyed writing it, and using it.

Now, don't all download it at once, there's plenty for everyone...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7029981-116672684299420916?l=fawcett.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.call-with-current-continuation.org/eggs/' title='hart 0.1 (or, Man lays egg, news at 11)'/><link rel='replies' type='application/atom+xml' href='http://fawcett.blogspot.com/feeds/116672684299420916/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7029981&amp;postID=116672684299420916' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/116672684299420916'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/116672684299420916'/><link rel='alternate' type='text/html' href='http://fawcett.blogspot.com/2006/12/hart-01-or-man-lays-egg-news-at-11.html' title='hart 0.1 (or, Man lays egg, news at 11)'/><author><name>fawcett</name><uri>http://www.blogger.com/profile/03670078611251538949</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7029981.post-116671708461240522</id><published>2006-12-21T11:04:00.000-05:00</published><updated>2006-12-21T11:04:44.623-05:00</updated><title type='text'>...minus Chicken, plus Io, ...</title><content type='html'>A &lt;a href="http://www.iolanguage.com/blog/blog.cgi?do=item&amp;amp;id=91"&gt;delightful rewrite in Io&lt;/a&gt; of my &lt;a href="http://fawcett.blogspot.com/2006/12/haskell-rss-aggregator-chicken-haskell.html"&gt;rewrite in Scheme&lt;/a&gt; of this &lt;a href="http://cale.yi.org/index.php/HRSS"&gt;Haskell RSS aggregator&lt;/a&gt;. For me, the  take-away is the sheer beauty of the functional style of programming.

By the way, I'd love to see a rewrite in &lt;a href="http://www.factorcode.org/"&gt;Factor&lt;/a&gt;, if anyone's listening... :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7029981-116671708461240522?l=fawcett.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.iolanguage.com/blog/blog.cgi?do=item&amp;id=91' title='...minus Chicken, plus Io, ...'/><link rel='replies' type='application/atom+xml' href='http://fawcett.blogspot.com/feeds/116671708461240522/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7029981&amp;postID=116671708461240522' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/116671708461240522'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/116671708461240522'/><link rel='alternate' type='text/html' href='http://fawcett.blogspot.com/2006/12/minus-chicken-plus-io.html' title='...minus Chicken, plus Io, ...'/><author><name>fawcett</name><uri>http://www.blogger.com/profile/03670078611251538949</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7029981.post-116655605809396532</id><published>2006-12-19T14:12:00.000-05:00</published><updated>2006-12-19T22:15:49.716-05:00</updated><title type='text'>Haskell + RSS Aggregator + Chicken − Haskell...</title><content type='html'>This little example of &lt;a href="http://cale.yi.org/index.php/HRSS"&gt;an RSS aggregator&lt;/a&gt; written in &lt;a href="http://haskell.org/"&gt;Haskell&lt;/a&gt; got my curiosity piqued: what would a work-alike in Scheme look like? My motivation was &lt;em&gt;not&lt;/em&gt; to write a shorter version, or even a better one really. I did want to kick the tires of "Hart", my soon-to-be-released HTML-generation module for &lt;a href="http://www.call-with-current-continuation.org/"&gt;Chicken Scheme&lt;/a&gt;... the more real-world use, the better.
&lt;p&gt;Here's the code (&lt;a href="http://fawcett.medialab.uwindsor.ca/scheme/srss.scm"&gt;download source&lt;/a&gt;):
&lt;/p&gt;&lt;pre style="border-style: solid none solid solid; border-color: rgb(187, 187, 187) -moz-use-text-color rgb(187, 187, 187) rgb(187, 187, 187); border-width: 1px medium 1px 1px; margin: 1em; padding: 1em; overflow: scroll; height: 30em; width:550px;"&gt;&lt;span class="quack-pltish-comment"&gt;;; srss.scm -- simple RSS aggregator in Chicken Scheme
&lt;/span&gt;
&lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;use http-client              &lt;span class="quack-pltish-comment"&gt;; to fetch the feeds
&lt;/span&gt;     ssax                     &lt;span class="quack-pltish-comment"&gt;; XML parsing
&lt;/span&gt;     sxml-tools               &lt;span class="quack-pltish-comment"&gt;; XPath-like functions (sxpath)
&lt;/span&gt;     hart&lt;span class="cparen-normal-paren"&gt;)&lt;/span&gt;                    &lt;span class="quack-pltish-comment"&gt;; html generation (mine; not public yet)
&lt;/span&gt;
&lt;span class="cparen-around-define"&gt;(&lt;/span&gt;&lt;span class="quack-pltish-keyword"&gt;define&lt;/span&gt; &lt;span class="cparen-binding-list"&gt;(&lt;/span&gt;&lt;span class="quack-pltish-defn"&gt;url-&amp;gt;dom&lt;/span&gt; url&lt;span class="cparen-binding-list"&gt;)&lt;/span&gt;
  &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;SSAX:XML-&amp;gt;SXML &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;nth-value &lt;span class="quack-pltish-selfeval"&gt;2&lt;/span&gt; &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;http:send-request url&lt;span class="cparen-normal-paren"&gt;))&lt;/span&gt; '&lt;span class="cparen-around-quote"&gt;()&lt;/span&gt;&lt;span class="cparen-normal-paren"&gt;)&lt;/span&gt;&lt;span class="cparen-around-define"&gt;)&lt;/span&gt;

&lt;span class="cparen-around-define"&gt;(&lt;/span&gt;&lt;span class="quack-pltish-keyword"&gt;define-macro&lt;/span&gt; &lt;span class="cparen-binding-list"&gt;(&lt;/span&gt;&lt;span class="quack-pltish-defn"&gt;&amp;gt;&amp;gt;&lt;/span&gt; obj . rest&lt;span class="cparen-binding-list"&gt;)&lt;/span&gt;
  &lt;span class="quack-pltish-comment"&gt;;; just a little sugar for getting text from XML nodes.
&lt;/span&gt;  &lt;span class="quack-pltish-comment"&gt;;; e.g. (&amp;gt;&amp;gt; my-htmldoc html head title) --&amp;gt; "doc title"
&lt;/span&gt;  &lt;span class="quack-pltish-comment"&gt;;;      (&amp;gt;&amp;gt; my-htmldoc // h1) --&amp;gt; "first main heading"
&lt;/span&gt;  `&lt;span class="cparen-around-letdo"&gt;(&lt;/span&gt;&lt;span class="quack-pltish-keyword"&gt;let&lt;/span&gt; &lt;span class="cparen-binding-list"&gt;(&lt;/span&gt;&lt;span class="cparen-binding"&gt;(&lt;/span&gt;lst &lt;span class="cparen-normal-paren"&gt;((&lt;/span&gt;sxpath '&lt;span class="cparen-around-quote"&gt;(&lt;/span&gt;,@rest *text*&lt;span class="cparen-around-quote"&gt;)&lt;/span&gt;&lt;span class="cparen-normal-paren"&gt;)&lt;/span&gt; ,obj&lt;span class="cparen-normal-paren"&gt;)&lt;/span&gt;&lt;span class="cparen-binding"&gt;)&lt;/span&gt;&lt;span class="cparen-binding-list"&gt;)&lt;/span&gt;
     &lt;span class="cparen-around-conditional"&gt;(&lt;/span&gt;&lt;span class="quack-pltish-keyword"&gt;if&lt;/span&gt; &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;null? lst&lt;span class="cparen-normal-paren"&gt;)&lt;/span&gt; &lt;span class="quack-pltish-selfeval"&gt;""&lt;/span&gt; &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;first lst&lt;span class="cparen-normal-paren"&gt;)&lt;/span&gt;&lt;span class="cparen-around-conditional"&gt;)&lt;/span&gt;&lt;span class="cparen-around-letdo"&gt;)&lt;/span&gt;&lt;span class="cparen-around-define"&gt;)&lt;/span&gt;

&lt;span class="cparen-around-define"&gt;(&lt;/span&gt;&lt;span class="quack-pltish-keyword"&gt;define&lt;/span&gt; &lt;span class="quack-pltish-defn"&gt;get-items&lt;/span&gt; &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;sxpath '&lt;span class="cparen-around-quote"&gt;(&lt;/span&gt;* * item&lt;span class="cparen-around-quote"&gt;)&lt;/span&gt;&lt;span class="cparen-normal-paren"&gt;)&lt;/span&gt;&lt;span class="cparen-around-define"&gt;)&lt;/span&gt;

&lt;span class="cparen-around-define"&gt;(&lt;/span&gt;&lt;span class="quack-pltish-keyword"&gt;define&lt;/span&gt; &lt;span class="cparen-binding-list"&gt;(&lt;/span&gt;&lt;span class="quack-pltish-defn"&gt;aggregate&lt;/span&gt; urls&lt;span class="cparen-binding-list"&gt;)&lt;/span&gt;
  &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;hart &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;html
         &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;head &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;title &lt;span class="quack-pltish-selfeval"&gt;"RSS Aggregate"&lt;/span&gt;&lt;span class="cparen-normal-paren"&gt;)&lt;/span&gt;
               &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;link &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;@ &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;rel &lt;span class="quack-pltish-selfeval"&gt;"stylesheet"&lt;/span&gt;&lt;span class="cparen-normal-paren"&gt;)&lt;/span&gt;
                        &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;href &lt;span class="quack-pltish-selfeval"&gt;"srss.css"&lt;/span&gt;&lt;span class="cparen-normal-paren"&gt;)&lt;/span&gt; &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;type &lt;span class="quack-pltish-selfeval"&gt;"text/css"&lt;/span&gt;&lt;span class="cparen-normal-paren"&gt;))))&lt;/span&gt;
         &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;body &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;h1 &lt;span class="quack-pltish-selfeval"&gt;"RSS Aggregate"&lt;/span&gt;&lt;span class="cparen-normal-paren"&gt;)&lt;/span&gt;
               &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;for: &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;dom &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;map url-&amp;gt;dom urls&lt;span class="cparen-normal-paren"&gt;))&lt;/span&gt;
                 &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;div &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;@ &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;&lt;span class="quack-pltish-keyword"&gt;class&lt;/span&gt; &lt;span class="quack-pltish-selfeval"&gt;"channel"&lt;/span&gt;&lt;span class="cparen-normal-paren"&gt;))&lt;/span&gt;
                      &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;h2 &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;raw: &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;&amp;gt;&amp;gt; dom * * title&lt;span class="cparen-normal-paren"&gt;)))&lt;/span&gt;
                      &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;ul &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;for: &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;item &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;get-items dom&lt;span class="cparen-normal-paren"&gt;))&lt;/span&gt;
                            &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;li &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;@ &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;&lt;span class="quack-pltish-keyword"&gt;class&lt;/span&gt; &lt;span class="quack-pltish-selfeval"&gt;"item"&lt;/span&gt;&lt;span class="cparen-normal-paren"&gt;))&lt;/span&gt;
                                &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;a &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;@ &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;href &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;&amp;gt;&amp;gt; item link&lt;span class="cparen-normal-paren"&gt;)))&lt;/span&gt;
                                   &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;h3 &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;raw: &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;&amp;gt;&amp;gt; item title&lt;span class="cparen-normal-paren"&gt;))))&lt;/span&gt;
                                &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;p &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;raw: &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;&amp;gt;&amp;gt; item description&lt;span class="cparen-normal-paren"&gt;)))))))))))&lt;/span&gt;&lt;span class="cparen-around-define"&gt;)&lt;/span&gt;

&lt;span class="quack-pltish-comment"&gt;;; main
&lt;/span&gt;
&lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;match &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;command-line-arguments&lt;span class="cparen-normal-paren"&gt;)&lt;/span&gt;
  &lt;span class="cparen-normal-paren"&gt;((&lt;/span&gt;out-file . urls&lt;span class="cparen-normal-paren"&gt;)&lt;/span&gt; &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;with-output-to-file out-file 
                       &lt;span class="cparen-around-lambda"&gt;(&lt;/span&gt;&lt;span class="quack-pltish-keyword"&gt;lambda&lt;/span&gt; &lt;span class="cparen-binding-list"&gt;()&lt;/span&gt; &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;aggregate urls&lt;span class="cparen-normal-paren"&gt;)&lt;/span&gt;&lt;span class="cparen-around-lambda"&gt;)&lt;/span&gt;&lt;span class="cparen-normal-paren"&gt;))&lt;/span&gt;
  &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;_ &lt;span class="cparen-normal-paren"&gt;(&lt;/span&gt;print &lt;span class="quack-pltish-selfeval"&gt;"usage: srss outfile [url]*"&lt;/span&gt;&lt;span class="cparen-normal-paren"&gt;)))&lt;/span&gt;
&lt;/pre&gt;
Since I haven't released Hart quite yet, I guess you'll have to trust me that it works. ;-) The Hart-part above is the stuff that looks like &lt;code&gt;(ul (for: (item items) (li (text: item)))) &lt;/code&gt;— my ultra-clever new technology turns expressions like that into efficient HTML/XML-generating code. Without Hart, a similar approach in Scheme would be to build up a DOM-like &lt;a href="http://okmij.org/ftp/Scheme/SXML.html"&gt;SXML&lt;/a&gt; structure, and serialize it to XML; the SXML-based code would look pretty similar.
&lt;p&gt;I sort of like how the program looks a bit like XSLT cross-bred with a more-traditional "scripting" language. Sort of. I won't push the analogy too far; I'm sure that to many it looks more like &lt;a href="http://www.linuxjournal.com/article/2070"&gt; oatmeal with fingernail clippings mixed in&lt;/a&gt;. I do think it's a touch more readable (though not by much) than the Haskell program it mimics.
&lt;/p&gt;&lt;p&gt;Like the Haskell program, it could benefit from more documentation, especially for readers unfamiliar with Scheme. (If anyone asks, I'll publish a step-by-step breakdown of the program to cover all the Schemely goodness therein.)&lt;/p&gt;Anyway… I came, I saw, I transliterated a short Haskell program. QED. If you're in a meme-perpetuating mood, maybe you'd like to code the same thing up in the language of your choice, and share it?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7029981-116655605809396532?l=fawcett.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fawcett.blogspot.com/feeds/116655605809396532/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7029981&amp;postID=116655605809396532' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/116655605809396532'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/116655605809396532'/><link rel='alternate' type='text/html' href='http://fawcett.blogspot.com/2006/12/haskell-rss-aggregator-chicken-haskell.html' title='Haskell + RSS Aggregator + Chicken &amp;minus; Haskell...'/><author><name>fawcett</name><uri>http://www.blogger.com/profile/03670078611251538949</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7029981.post-116188318565884702</id><published>2006-10-26T13:19:00.000-04:00</published><updated>2006-10-26T13:23:36.256-04:00</updated><title type='text'>The Search for the Common Lisp</title><content type='html'>I've set up a &lt;a href="http://www.google.com/coop/cse?cx=012679172708151972086%3Aeg1jtvm_dlk"&gt;Common Lisp Search Engine&lt;/a&gt; using Google's new &lt;a href="http://www.google.com/coop/"&gt;Co-op&lt;/a&gt; service. Some folks from the CL community have already joined in as contributors — thanks, and welcome! So if you're in the market for a &lt;a href="http://www.google.com/custom?cx=012679172708151972086%3Aeg1jtvm_dlk&amp;q=car&amp;amp;sa=Search&amp;cof=CX%3ACommon%2520Lisp%2520Search%2520Engine%3BFORID%3A0&amp;amp;hl=en&amp;client=google-coop"&gt;CAR&lt;/a&gt;, but not the &lt;a href="http://www.google.ca/search?q=car"&gt;four-wheeled kind&lt;/a&gt;, this might be worth a look.

I'm not 100% sure about Co-op's collaborative features yet. I wanted to invite anyone and everyone to join in as a collaborator, if they wanted to. I figured I would start things rolling, and then get out of the way and let the community take over. But it seems that I'm the "owner" of the engine, and anyone else who joins in (1) needs my permission to do so, and (2) doesn't get unfettered management access. Still, it's only a beta; I'm sure these wrinkles will work themselves out.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7029981-116188318565884702?l=fawcett.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.google.com/coop/cse?cx=012679172708151972086%3Aeg1jtvm_dlk' title='The Search for the Common Lisp'/><link rel='replies' type='application/atom+xml' href='http://fawcett.blogspot.com/feeds/116188318565884702/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7029981&amp;postID=116188318565884702' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/116188318565884702'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/116188318565884702'/><link rel='alternate' type='text/html' href='http://fawcett.blogspot.com/2006/10/search-for-common-lisp.html' title='The Search for the Common Lisp'/><author><name>fawcett</name><uri>http://www.blogger.com/profile/03670078611251538949</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7029981.post-116179376179266286</id><published>2006-10-25T12:29:00.000-04:00</published><updated>2006-10-25T12:33:16.866-04:00</updated><title type='text'>Functional optimizations with PyPy</title><content type='html'>A &lt;a href="http://radeex.blogspot.com/2006/10/functional-optimizations-with-pypy.html"&gt;wickedly clever idea&lt;/a&gt; for Python, from Christopher Armstrong:
&lt;blockquote&gt;If you think that's crazy, you can &lt;span style="font-style: italic;"&gt;also&lt;/span&gt; take a look at &lt;a href="http://home.pipeline.com/%7Ehbaker1/Use1Var.html"&gt;Use-Once-Variables&lt;/a&gt;, an idea for adding "linear objects" to non-linear languages (pretty much all programming languages are non-linear). Use-once-variables refer to linear objects, which can &lt;span style="font-style: italic;"&gt;only&lt;/span&gt; ever have one reference at a time. Copying and deletion both require explicit support from the linear object. It definitely changes your style of programming to use linear objects, but they can offer some serious improvements to the ability to reason about code that uses them, not to mention that you can optimize the crap out of them.&lt;/blockquote&gt;Henry Baker, author of the paper that Armstrong cites, also &lt;a href="http://home.pipeline.com/%7Ehbaker1/CheneyMTA.html"&gt;inspired&lt;/a&gt; the excellent &lt;a href="http://www.call-with-current-continuation.org/"&gt;Chicken Scheme&lt;/a&gt; implementation. His&lt;span style="text-decoration: underline;"&gt; &lt;/span&gt;&lt;a href="http://home.pipeline.com/%7Ehbaker1/"&gt;work&lt;/a&gt; would make good rainy-day reading, one of these rainy days.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7029981-116179376179266286?l=fawcett.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://radeex.blogspot.com/2006/10/functional-optimizations-with-pypy.html' title='Functional optimizations with PyPy'/><link rel='replies' type='application/atom+xml' href='http://fawcett.blogspot.com/feeds/116179376179266286/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7029981&amp;postID=116179376179266286' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/116179376179266286'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/116179376179266286'/><link rel='alternate' type='text/html' href='http://fawcett.blogspot.com/2006/10/functional-optimizations-with-pypy.html' title='Functional optimizations with PyPy'/><author><name>fawcett</name><uri>http://www.blogger.com/profile/03670078611251538949</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7029981.post-116068024809504107</id><published>2006-10-12T15:10:00.000-04:00</published><updated>2006-10-12T15:12:14.820-04:00</updated><title type='text'>SQLite Full Text Search</title><content type='html'>&lt;a href="http://blog.cleverly.com/permalinks/247.html"&gt;&lt;/a&gt;&lt;blockquote&gt;&lt;a href="http://blog.cleverly.com/permalinks/247.html"&gt;SQLite Keynote&lt;/a&gt;: "Full Text Search (version 1) built into SQLite 3.3.8 released Monday. As of SQLite 3.3.8 (released Monday!) full text search support in SQLite. Ricahrd's authorized to announce help from engineers at Google. Later question elicited that roughly half of the FTS code was written by him &amp; Dan, the other half by four engineers [didn't have time to write down their names] from Google. He isn't able/can't comment on their motivations/plans/internal usage. (Obviouslyy won't be replacing their search engine with SQLite.)"&lt;/blockquote&gt;Very cool; thanks to &lt;a href="http://onebiglibrary.net/"&gt;dchud&lt;/a&gt; for pointing it out.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7029981-116068024809504107?l=fawcett.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://blog.cleverly.com/permalinks/247.html' title='SQLite Full Text Search'/><link rel='replies' type='application/atom+xml' href='http://fawcett.blogspot.com/feeds/116068024809504107/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7029981&amp;postID=116068024809504107' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/116068024809504107'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/116068024809504107'/><link rel='alternate' type='text/html' href='http://fawcett.blogspot.com/2006/10/sqlite-full-text-search.html' title='SQLite Full Text Search'/><author><name>fawcett</name><uri>http://www.blogger.com/profile/03670078611251538949</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7029981.post-115315901606184191</id><published>2006-07-17T13:56:00.000-04:00</published><updated>2006-10-09T09:26:17.346-04:00</updated><title type='text'>The Cog is back</title><content type='html'>&lt;blockquote&gt;…for now, this is just a note to say that this blog still has a heartbeat and will hopefully have a more interesting ecosystem to live in soon.&lt;/blockquote&gt;Art Rhyno's &lt;a href="http://librarycog.uwindsor.ca:8087/artblog/librarycog/"&gt;librarycog&lt;/a&gt; is back in business. His blog got slammed by a Squishdot problem a while back, and he has been working his OpenOffice/WebDAV/Cocoon mojo to get a &lt;a href="http://librarycog.uwindsor.ca:8087/artblog/librarycog/"&gt;new! improved! blog&lt;/a&gt; on the rails.

I'm very eager to see how his &lt;a href="http://librarycog.uwindsor.ca:8082/artblog/librarycog/1140463879"&gt;Web-presentation project&lt;/a&gt; pans out, so hopefully the new blog's cogs will keep turning, and Art can spend more time on the presentation stuff.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7029981-115315901606184191?l=fawcett.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://librarycog.uwindsor.ca:8087/artblog/librarycog/' title='The Cog is back'/><link rel='replies' type='application/atom+xml' href='http://fawcett.blogspot.com/feeds/115315901606184191/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7029981&amp;postID=115315901606184191' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/115315901606184191'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/115315901606184191'/><link rel='alternate' type='text/html' href='http://fawcett.blogspot.com/2006/07/cog-is-back.html' title='The Cog is back'/><author><name>fawcett</name><uri>http://www.blogger.com/profile/03670078611251538949</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7029981.post-115315062930180252</id><published>2006-07-17T11:37:00.002-04:00</published><updated>2009-11-14T13:40:29.470-05:00</updated><title type='text'>Boing Boing: Canadian Nat'l Film Board puts 50 classic shorts online</title><content type='html'>Wonderful -- I haven't seen some of these in years.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7029981-115315062930180252?l=fawcett.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.boingboing.net/2006/07/15/canadian_natl_film_b.html' title='Boing Boing: Canadian Nat&apos;l Film Board puts 50 classic shorts online'/><link rel='replies' type='application/atom+xml' href='http://fawcett.blogspot.com/feeds/115315062930180252/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7029981&amp;postID=115315062930180252' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/115315062930180252'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/115315062930180252'/><link rel='alternate' type='text/html' href='http://fawcett.blogspot.com/2006/07/boing-boing-canadian-natl-film-board.html' title='Boing Boing: Canadian Nat&apos;l Film Board puts 50 classic shorts online'/><author><name>fawcett</name><uri>http://www.blogger.com/profile/03670078611251538949</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7029981.post-115289960910737591</id><published>2006-07-14T13:53:00.000-04:00</published><updated>2006-07-14T13:53:29.390-04:00</updated><title type='text'>PicoTurbine: A home-brew wind turbine</title><content type='html'>This would be a heck of a great science project for the kids. The "solar stovetop" looks great too.

&lt;span style="text-decoration: underline;"&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7029981-115289960910737591?l=fawcett.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.picoturbine.com/' title='PicoTurbine: A home-brew wind turbine'/><link rel='replies' type='application/atom+xml' href='http://fawcett.blogspot.com/feeds/115289960910737591/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7029981&amp;postID=115289960910737591' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/115289960910737591'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/115289960910737591'/><link rel='alternate' type='text/html' href='http://fawcett.blogspot.com/2006/07/picoturbine-home-brew-wind-turbine.html' title='PicoTurbine: A home-brew wind turbine'/><author><name>fawcett</name><uri>http://www.blogger.com/profile/03670078611251538949</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7029981.post-115281653015154332</id><published>2006-07-13T14:48:00.000-04:00</published><updated>2006-07-13T14:48:50.240-04:00</updated><title type='text'>Nexenta GNU/OpenSolaris</title><content type='html'>I've been meaning to get up to speed on Solaris—we're predominantly a Solaris campus here in Windsor—and this might be an interesting way to do it. &lt;a href="http://www.gnusolaris.org/gswiki/Nexenta_OS"&gt;Nexenta GNU/OpenSolaris&lt;/a&gt; is the OpenSolaris kernel, on Intel, with a GNU userspace: specifically, Ubuntu Dapper! Interesting stuff.

It looks like OpenSolaris &lt;a href="http://www.gnusolaris.org/gswiki/FAQ#head-57c10728d34a3a6551ba06167034804c236f7cc9"&gt;doesn't support the hardware&lt;/a&gt; that many of our servers use, so I'm not sure we'll be using it in deployment any time soon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7029981-115281653015154332?l=fawcett.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.gnusolaris.org/gswiki/Nexenta_OS' title='Nexenta GNU/OpenSolaris'/><link rel='replies' type='application/atom+xml' href='http://fawcett.blogspot.com/feeds/115281653015154332/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7029981&amp;postID=115281653015154332' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/115281653015154332'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/115281653015154332'/><link rel='alternate' type='text/html' href='http://fawcett.blogspot.com/2006/07/nexenta-gnuopensolaris.html' title='Nexenta GNU/OpenSolaris'/><author><name>fawcett</name><uri>http://www.blogger.com/profile/03670078611251538949</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7029981.post-115279774465515603</id><published>2006-07-13T09:35:00.000-04:00</published><updated>2006-07-13T09:35:45.236-04:00</updated><title type='text'>Database Soup: Primary Keyvil</title><content type='html'>&lt;a href="http://blogs.ittoolbox.com/database/soup/archives/primary-keyvil-part-i-7327"&gt;Primary Keyvil, Part I&lt;/a&gt;, &lt;a href="http://blogs.ittoolbox.com/database/soup/archives/primary-keyvil-part-ii-7345"&gt;Part II&lt;/a&gt;,  and &lt;a href="http://blogs.ittoolbox.com/database/soup/archives/primary-keyvil-part-iii-7365"&gt;Part III&lt;/a&gt;.
&lt;span id="intelliTXT"&gt;&lt;blockquote&gt;Worse yet, even RDBMS book authors are instructing their readers to "always include an ID column," suturing this misunderstanding into the body of industry knowledge like a badly wired cybernetic implant.&lt;/blockquote&gt;&lt;/span&gt;A very good read. I've certainly fallen into this trap, more times than not.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7029981-115279774465515603?l=fawcett.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://blogs.ittoolbox.com/database/soup/archives/primary-keyvil-part-i-7327' title='Database Soup: Primary Keyvil'/><link rel='replies' type='application/atom+xml' href='http://fawcett.blogspot.com/feeds/115279774465515603/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7029981&amp;postID=115279774465515603' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/115279774465515603'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/115279774465515603'/><link rel='alternate' type='text/html' href='http://fawcett.blogspot.com/2006/07/database-soup-primary-keyvil.html' title='Database Soup: Primary Keyvil'/><author><name>fawcett</name><uri>http://www.blogger.com/profile/03670078611251538949</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7029981.post-115160432682553698</id><published>2006-06-29T14:05:00.000-04:00</published><updated>2006-06-29T14:15:05.273-04:00</updated><title type='text'>sxpath quick reference</title><content type='html'>I've just put up an augmented version of Kirill Lisovsky's &lt;a href="http://www196.pair.com/lisovsky/query/examples/xpath/"&gt;SXPath examples&lt;/a&gt; that includes the &lt;a href="http://www196.pair.com/lisovsky/query/sxpath/"&gt;SXPath&lt;/a&gt; expressions beside the XPath ones, for quick reference. &lt;a href="http://fawcett.medialab.uwindsor.ca/specs/sxpath/examples.html"&gt;Enjoy&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7029981-115160432682553698?l=fawcett.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://fawcett.medialab.uwindsor.ca/specs/sxpath/examples.html' title='sxpath quick reference'/><link rel='replies' type='application/atom+xml' href='http://fawcett.blogspot.com/feeds/115160432682553698/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7029981&amp;postID=115160432682553698' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/115160432682553698'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/115160432682553698'/><link rel='alternate' type='text/html' href='http://fawcett.blogspot.com/2006/06/sxpath-quick-reference.html' title='sxpath quick reference'/><author><name>fawcett</name><uri>http://www.blogger.com/profile/03670078611251538949</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7029981.post-115134765436857787</id><published>2006-06-26T14:47:00.000-04:00</published><updated>2006-06-26T14:49:51.703-04:00</updated><title type='text'>Python-dev: switch, case, static, oh my.</title><content type='html'>It makes sense to add more syntax to languages that are &lt;a href="http://en.wikipedia.org/wiki/Scheme_programming_language"&gt;intentionally sparse&lt;/a&gt; or &lt;a href="http://java.sun.com/"&gt;unfortunately verbose&lt;/a&gt;. In the former case, it's the developers who get to add their own syntax, building a custom language up as their program grows, and that's a good thing. In the latter, the owner of the language occasionally bows to the demands of the user-base, and adds a cleaner looping construct, or tosses in a new type or two; and that's good for the masses, I guess, a bit of margarine to make the dry bread go down easier.

But when you've already hit a syntactic sweet-spot, it's time to put down the icing-gun and step away from the darn cake.

At the nitpicky level, I think it's the &lt;code&gt;static&lt;/code&gt; modifier that gets me more than the &lt;code&gt;case&lt;/code&gt; construct, there's too much magic in that little word. But it's the macro-level Fooling With a Good Thing that really makes me sad.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7029981-115134765436857787?l=fawcett.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://mail.python.org/pipermail/python-dev/2006-June/066409.html' title='Python-dev: switch, case, static, oh my.'/><link rel='replies' type='application/atom+xml' href='http://fawcett.blogspot.com/feeds/115134765436857787/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7029981&amp;postID=115134765436857787' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/115134765436857787'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/115134765436857787'/><link rel='alternate' type='text/html' href='http://fawcett.blogspot.com/2006/06/python-dev-switch-case-static-oh-my.html' title='Python-dev: switch, case, static, oh my.'/><author><name>fawcett</name><uri>http://www.blogger.com/profile/03670078611251538949</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7029981.post-115030161692041455</id><published>2006-06-14T12:13:00.000-04:00</published><updated>2006-06-14T14:41:06.616-04:00</updated><title type='text'>Outed.</title><content type='html'>Aw geez, someone I actually know and respect &lt;a href="http://onebiglibrary.net/story/annual-language-schedule-2006#comment-233"&gt;just found this blog&lt;/a&gt;. I guess that if I wanted anonymity, I should have used a pseudonym when I set it up. Alas, &lt;code&gt;HelloKitty351&lt;/code&gt; was taken, and anything else simply would not do.

I've been meaning for some time to start posting stuff here. I suppose I'd make a good blogger -- I am occasionally insightful, frequently immodest, and am comfortable making a total fool out of myself in public. The other criterion is Available Time, which is always in short supply, but maybe I can squeeze a few more cycles out of the box.

No promises Dan, but I'll try.  :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7029981-115030161692041455?l=fawcett.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://onebiglibrary.net/story/annual-language-schedule-2006#comment-233' title='Outed.'/><link rel='replies' type='application/atom+xml' href='http://fawcett.blogspot.com/feeds/115030161692041455/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7029981&amp;postID=115030161692041455' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/115030161692041455'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/115030161692041455'/><link rel='alternate' type='text/html' href='http://fawcett.blogspot.com/2006/06/outed.html' title='Outed.'/><author><name>fawcett</name><uri>http://www.blogger.com/profile/03670078611251538949</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7029981.post-111884357077907780</id><published>2005-06-15T09:52:00.000-04:00</published><updated>2006-06-14T14:06:33.900-04:00</updated><title type='text'>"Boring files" in darcs</title><content type='html'>I need to memorize this one...

&lt;a href="http://www.darcs.net/manual/node5.html#SECTION00510040000000000000"&gt;Configuring darcs&lt;/a&gt;: "You may want to have the boring file under version control. To do this you can use darcs setpref to set the value ``boringfile'' to the name of your desired boring file (e.g. ``&lt;span style="color: rgb(128, 0, 0); font-weight: bold;"&gt;darcs setpref boringfile .boring&lt;/span&gt;'', where .boring is the repository path of a file that has been darcs added to your repository). The boringfile pref overrides _darcs/prefs/boring, so be sure to copy that file to the boringfile."&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7029981-111884357077907780?l=fawcett.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.darcs.net/manual/node5.html#SECTION00510040000000000000' title='&quot;Boring files&quot; in darcs'/><link rel='replies' type='application/atom+xml' href='http://fawcett.blogspot.com/feeds/111884357077907780/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7029981&amp;postID=111884357077907780' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/111884357077907780'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/111884357077907780'/><link rel='alternate' type='text/html' href='http://fawcett.blogspot.com/2005/06/boring-files-in-darcs.html' title='&quot;Boring files&quot; in darcs'/><author><name>fawcett</name><uri>http://www.blogger.com/profile/03670078611251538949</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7029981.post-111868494896457293</id><published>2005-06-13T13:49:00.000-04:00</published><updated>2005-06-13T13:49:08.970-04:00</updated><title type='text'>Oddmuse</title><content type='html'>Yet another Wiki I should look at.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7029981-111868494896457293?l=fawcett.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.oddmuse.org/cgi-bin/wiki' title='Oddmuse'/><link rel='replies' type='application/atom+xml' href='http://fawcett.blogspot.com/feeds/111868494896457293/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7029981&amp;postID=111868494896457293' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/111868494896457293'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/111868494896457293'/><link rel='alternate' type='text/html' href='http://fawcett.blogspot.com/2005/06/oddmuse.html' title='Oddmuse'/><author><name>fawcett</name><uri>http://www.blogger.com/profile/03670078611251538949</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7029981.post-111834808749308958</id><published>2005-06-09T15:42:00.000-04:00</published><updated>2005-06-09T16:25:14.360-04:00</updated><title type='text'>Tearing down markup</title><content type='html'>&lt;p&gt;A project crossed my desk a few days ago that's screaming for a Wiki implementation. There's a broad, loosely defined set of authors; pages will be short and light on style; there are no pre-set navigation paths through the material. I've looked at &lt;a href="http://en.wikipedia.org/wiki/MediaWiki"&gt;a few&lt;/a&gt; &lt;a href="http://moinmoin.wikiwikiweb.de/"&gt;well-known&lt;/a&gt; &lt;a href="http://www.twiki.org/"&gt;Wiki packages&lt;/a&gt; but they all leave something out; none of them quite matches what I had in mind for this project. So I'm left with the developer's dilemma: do you wedge the solution into a poorly-fitting framework, or do you roll your own?&lt;a href="http://opensource.atlassian.com/confluence/spring/homepage.action"&gt;&lt;/a&gt;
&lt;/p&gt; &lt;p&gt;One of the features I really want is a clean extensibility scheme. I want to be able to specify custom actions on marked-up segments of the document, kind of like TWiki plugins; but without the horrible TWiki syntax. For example, I'd like to be able to add a self-assessment quiz at the bottom of a page:
&lt;/p&gt;&lt;pre&gt;&lt;code&gt;{include-quiz:
{What's the most important feature in a Wiki?}
{Clean markup language | Markup is important, but...}
{Permissions-based editing | There are times when...}
{Revision history | While a crucial feature...}
{All of the above | CORRECT | Yes! They are all...}}
&lt;/code&gt;&lt;/pre&gt;and, in its place, render up a nice interactive exercise. What happens to the results is an open question: one possibility is the recommendation of further readings in the Wiki based on the user's response.
&lt;p&gt;If you've ever considered rolling your own Wiki or content management system, you've probably struggled over what markup language to choose. Every Wiki/CMS seems to sport some derivative of classic WikiMarkup. From &lt;a href="http://www.prefab.com/ssl/notagsmarkup.html"&gt;No-Tags Markup&lt;/a&gt;:&lt;/p&gt; &lt;blockquote&gt;Similar systems that appear to have an active base of users:&lt;p&gt;&lt;/p&gt;   &lt;ul&gt; &lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Wiki_markup"&gt;wiki markup&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.zope.org/Members/jim/StructuredTextWiki/FrontPage"&gt;STX&lt;/a&gt; (Structured Text) by Jim Fulton, e.g. for Zope and ZWiki.&lt;/li&gt;&lt;li&gt;&lt;a href="http://docutils.sourceforge.net/rst.html"&gt;reStructuredText&lt;/a&gt; for Python's DocUtils.&lt;/li&gt;&lt;li&gt;&lt;a href="http://daringfireball.net/projects/markdown/"&gt;Markdown&lt;/a&gt; by John Gruber of Daring Fireball, including an active &lt;a href="http://six.pairlist.net/mailman/listinfo/markdown-discuss"&gt;mailing list&lt;/a&gt;.&lt;/li&gt; &lt;/ul&gt;    &lt;p&gt;Plus a few that may not have caught on much beyond the original developer:&lt;/p&gt;  &lt;ul&gt; &lt;li&gt;&lt;a href="http://www-106.ibm.com/developerworks/xml/library/x-tipt2dw.html"&gt;txt2dw&lt;/a&gt; used at IBM DeveloperWorks.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.fourmilab.ch/etexts/etset/"&gt;etset&lt;/a&gt; including public domain software for converting text to HTML or Palm Markup Language via LaTeX.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.metamage.com/proj/mml.html"&gt;MML&lt;/a&gt; (Modest Markup Language) by Joshua Juran.&lt;/li&gt;&lt;li&gt;&lt;a href="http://textism.com/tools/textile/"&gt;textile&lt;/a&gt; "A Humane Web Text Generator".&lt;/li&gt;&lt;li&gt;&lt;a href="http://rx4rdf.liminalzone.org/RhizML"&gt;ZML&lt;/a&gt; (formerly known as RhizML) for the &lt;a href="http://rx4rdf.liminalzone.org/Rhizome"&gt;Rhizome&lt;/a&gt; content management system.&lt;/li&gt;&lt;/ul&gt;&lt;/blockquote&gt;Each one has a different motivation. Markdown cares more about human readability than, say, MML. But that's just a nice way of saying that they all suck in their own special way.

And yet, I'm considering adding another one to the list.

First, I'd like to have a clean separation between the front-end syntactic idiosyncrasies, and the back-end processing: Markdown to HTML, e.g., is no help. There needs to be an intermediary format. The intermediary should be serializable, and definitely language independent: XML or S-expressions would be fine. I don't see a burning need to convert the intermediary format back into front-end markup, though maybe I'll rethink that later.

What's the payoff? When I, inevitably, realize that My Markup Sucks Just as Bad, I can work on alternate syntaxes without throwing out my whole system. I can also write different backends, to publish to PDF, etc. I can manipulate the AST, treat parts of it as a program and execute it, etc.

I'm tempted to write my initial syntax in an sexpr style, kind of like the quiz example above. It's easy to parse; resembles the AST somewhat; and all the other benefits of sexprs (ask your local &lt;a href="http://c2.com/cgi/wiki?SmugLispWeenie"&gt;SmugLispWeenie&lt;/a&gt; for the exhaustive list).

If I end up writing such a beast, it's going to be in Python. It would make a cool Scheme project, but I don't want to throw away the vast sea of third-party modules available for Python.

My internal critic is sending up all kinds of warning flags, suggesting that maybe I should stop developing low-level bits that have already been hacked to death by others, and focus on high-level solutions. God, I hate that critic sometimes, but he's often right.

We'll see who wins. At the end of the day, I'm more interested in making a Wiki-like Thing that has cool features geared to the target audience, than in making a Wiki clone. So maybe the critic gets his walking papers, and I get to roll a new toy. Er, new application.

&lt;ul&gt;   &lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7029981-111834808749308958?l=fawcett.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://fawcett.blogspot.com/feeds/111834808749308958/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7029981&amp;postID=111834808749308958' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/111834808749308958'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7029981/posts/default/111834808749308958'/><link rel='alternate' type='text/html' href='http://fawcett.blogspot.com/2005/06/tearing-down-markup.html' title='Tearing down markup'/><author><name>fawcett</name><uri>http://www.blogger.com/profile/03670078611251538949</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
