<?xml version='1.0' encoding='UTF-8'?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'><id>tag:blogger.com,1999:blog-3363235268566529688</id><updated>2008-06-06T09:26:59.764-05:00</updated><title type='text'>Sustainable Code</title><link rel='alternate' type='text/html' href='http://sustainablecode.com/blog/'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default'/><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://sustainablecode.com/blog/atom.xml'/><author><name>Frederick Polgardy</name><uri>http://www.blogger.com/profile/11939486603262164846</uri><email>noreply@blogger.com</email></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>17</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3363235268566529688.post-8799988181692132758</id><published>2008-06-04T15:26:00.008-05:00</published><updated>2008-06-06T09:26:59.805-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='checkers'/><title type='text'>Checkers Rules in Lisp</title><content type='html'>I've recently started trying to wrap my head around the beast that is Lisp.  I have to say so far all the rumors are true -- just trying to program in it is enough to twist your brain into seeing problems in a whole new way.&lt;br /&gt;&lt;br /&gt;So with that minor disclaimer, I offer you my first semi-useful chunk of Lisp code: a set of functions and macros to generate the valid checkers moves and jumps given a 2-dimensional grid representing the current position.  I even exported the source from Emacs with syntax highlighting.  Ooooh.  Ahhhh.&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="code-block"&gt;(&lt;span class="keyword"&gt;defvar&lt;/span&gt; &lt;span class="variable-name"&gt;board&lt;/span&gt;&lt;br /&gt;  (make-array&lt;br /&gt;   '(8 8)&lt;br /&gt;   &lt;span class="builtin"&gt;:initial-contents&lt;/span&gt;&lt;br /&gt;   '(( 1  0  1  0  1  0  1  0)&lt;br /&gt;     ( 0  1  0  1  0  1  0  1)&lt;br /&gt;     ( 1  0  1  0  1  0  1  0)&lt;br /&gt;     ( 0 -1  0  0  0  0  0  0)  &lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;piece is out of place&lt;br /&gt;&lt;/span&gt;     ( 0  0  0  0  0  0  0  0)  &lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;to demonstrate jump&lt;br /&gt;&lt;/span&gt;     ( 0  0  0 -1  0 -1  0 -1)&lt;br /&gt;     (-1  0 -1  0 -1  0 -1  0)&lt;br /&gt;     ( 0 -1  0 -1  0 -1  0 -1))))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defvar&lt;/span&gt; &lt;span class="variable-name"&gt;*size*&lt;/span&gt; 8)&lt;br /&gt;(&lt;span class="keyword"&gt;defvar&lt;/span&gt; &lt;span class="variable-name"&gt;*red*&lt;/span&gt; 1)&lt;br /&gt;(&lt;span class="keyword"&gt;defvar&lt;/span&gt; &lt;span class="variable-name"&gt;*white*&lt;/span&gt; -1)&lt;br /&gt;(&lt;span class="keyword"&gt;defvar&lt;/span&gt; &lt;span class="variable-name"&gt;*empty*&lt;/span&gt; 0)&lt;br /&gt;(&lt;span class="keyword"&gt;defvar&lt;/span&gt; &lt;span class="variable-name"&gt;*red-piece*&lt;/span&gt; 1)&lt;br /&gt;(&lt;span class="keyword"&gt;defvar&lt;/span&gt; &lt;span class="variable-name"&gt;*white-piece*&lt;/span&gt; -1)&lt;br /&gt;(&lt;span class="keyword"&gt;defvar&lt;/span&gt; &lt;span class="variable-name"&gt;*red-king*&lt;/span&gt; 2)&lt;br /&gt;(&lt;span class="keyword"&gt;defvar&lt;/span&gt; &lt;span class="variable-name"&gt;*white-king*&lt;/span&gt; -2)&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defvar&lt;/span&gt; &lt;span class="variable-name"&gt;*side*&lt;/span&gt; *red*)&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;get-p&lt;/span&gt; (x y)&lt;br /&gt;  (aref board y x))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;set-p&lt;/span&gt; (x y p)&lt;br /&gt;  (setf (aref board y x) p))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;is-my-piece&lt;/span&gt; (p)&lt;br /&gt;  (= p *side*))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;is-my-king&lt;/span&gt; (p)&lt;br /&gt;  (= p (* 2 *side*)))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;is-mine&lt;/span&gt; (p)&lt;br /&gt;  (or (is-my-piece p) (is-my-king p)))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;is-opp&lt;/span&gt; (p)&lt;br /&gt;  (is-mine (- p)))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;is-empty&lt;/span&gt; (p)&lt;br /&gt;  (= p 0))&lt;br /&gt;&lt;br /&gt;&lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;(multi-loop ((for y from 0 to 7)&lt;br /&gt;&lt;/span&gt;&lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;             (for x from 0 to 7))&lt;br /&gt;&lt;/span&gt;&lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;            (print (list x y)))&lt;br /&gt;&lt;/span&gt;(&lt;span class="keyword"&gt;defmacro&lt;/span&gt; &lt;span class="function-name"&gt;multi-loop&lt;/span&gt; (loop-clauses body)&lt;br /&gt;  (&lt;span class="keyword"&gt;if&lt;/span&gt; (null loop-clauses)&lt;br /&gt;      body&lt;br /&gt;      (&lt;span class="keyword"&gt;let&lt;/span&gt; ((clause (car loop-clauses)))&lt;br /&gt;        `(&lt;span class="keyword"&gt;loop&lt;/span&gt; ,@clause do&lt;br /&gt;            (multi-loop ,(cdr loop-clauses) ,body)))))&lt;br /&gt;&lt;br /&gt;&lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;(each-square (x y p) body)&lt;br /&gt;&lt;/span&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;%each-square&lt;/span&gt; (f)&lt;br /&gt;  (multi-loop&lt;br /&gt;   ((for y from 0 below *size*)&lt;br /&gt;    (for x from 0 below *size*))&lt;br /&gt;   (&lt;span class="keyword"&gt;let&lt;/span&gt; ((p (get-p x y)))&lt;br /&gt;     (&lt;span class="keyword"&gt;if&lt;/span&gt; (= (mod (+ x y) 2) 0)&lt;br /&gt;         (funcall f x y p)))))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defmacro&lt;/span&gt; &lt;span class="function-name"&gt;each-square&lt;/span&gt; ((x y p) body)&lt;br /&gt;  `(%each-square (&lt;span class="keyword"&gt;lambda&lt;/span&gt; (,x ,y ,p) ,body)))&lt;br /&gt;&lt;br /&gt;&lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;(each-square-for-side (x y p) body)&lt;br /&gt;&lt;/span&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;%each-square-for-side&lt;/span&gt; (f)&lt;br /&gt;  (multi-loop&lt;br /&gt;   ((for y from 0 below *size*)&lt;br /&gt;    (for x from 0 below *size*))&lt;br /&gt;   (&lt;span class="keyword"&gt;let&lt;/span&gt; ((p (get-p x y)))&lt;br /&gt;     (&lt;span class="keyword"&gt;if&lt;/span&gt; (and (= (mod (+ x y) 2) 0)&lt;br /&gt;              (is-mine p))&lt;br /&gt;         (funcall f x y p)))))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defmacro&lt;/span&gt; &lt;span class="function-name"&gt;each-square-for-side&lt;/span&gt; ((x y p) body)&lt;br /&gt;  `(%each-square-for-side (&lt;span class="keyword"&gt;lambda&lt;/span&gt; (,x ,y ,p) ,body)))&lt;br /&gt;&lt;br /&gt;&lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;(each-jump-from x y (mx my nx ny) body)&lt;br /&gt;&lt;/span&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;%each-jump-from&lt;/span&gt; (x y f)&lt;br /&gt;  (&lt;span class="keyword"&gt;let&lt;/span&gt; ((p (get-p x y)))&lt;br /&gt;    (multi-loop&lt;br /&gt;     ((for dy in (&lt;span class="keyword"&gt;if&lt;/span&gt; (is-my-king p)&lt;br /&gt;                     (list *side* (- *side*))&lt;br /&gt;                     (list *side*)))&lt;br /&gt;      (for dx in (list *side* (- *side*))))&lt;br /&gt;     (&lt;span class="keyword"&gt;let&lt;/span&gt; ((mx (+ x dx))&lt;br /&gt;           (my (+ y dy))&lt;br /&gt;           (nx (+ x dx dx))&lt;br /&gt;           (ny (+ y dy dy)))&lt;br /&gt;       (&lt;span class="keyword"&gt;if&lt;/span&gt; (and (&amp;gt;= nx 0)&lt;br /&gt;                (&amp;lt; nx *size*)&lt;br /&gt;                (&amp;gt;= ny 0)&lt;br /&gt;                (&amp;lt; ny *size*)&lt;br /&gt;                (is-opp (get-p mx my))&lt;br /&gt;                (is-empty (get-p nx ny)))&lt;br /&gt;           (funcall f mx my nx ny))))))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defmacro&lt;/span&gt; &lt;span class="function-name"&gt;each-jump-from&lt;/span&gt; (x y (mx my nx ny) body)&lt;br /&gt;  `(%each-jump-from ,x ,y (&lt;span class="keyword"&gt;lambda&lt;/span&gt; (,mx ,my ,nx ,ny) ,body)))&lt;br /&gt;&lt;br /&gt;&lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;(each-move-from x y (nx ny) body)&lt;br /&gt;&lt;/span&gt;(&lt;span class="keyword"&gt;defun&lt;/span&gt; &lt;span class="function-name"&gt;%each-move-from&lt;/span&gt; (x y f)&lt;br /&gt;  (&lt;span class="keyword"&gt;let&lt;/span&gt; ((p (get-p x y)))&lt;br /&gt;    (multi-loop&lt;br /&gt;     ((for dy in (&lt;span class="keyword"&gt;if&lt;/span&gt; (is-my-king p)&lt;br /&gt;                     (list *side* (- *side*))&lt;br /&gt;                     (list *side*)))&lt;br /&gt;      (for dx in (list *side* (- *side*))))&lt;br /&gt;     (&lt;span class="keyword"&gt;let&lt;/span&gt; ((nx (+ x dx))&lt;br /&gt;           (ny (+ y dy)))&lt;br /&gt;       (&lt;span class="keyword"&gt;if&lt;/span&gt; (and (&amp;gt;= nx 0)&lt;br /&gt;                (&amp;lt; nx *size*)&lt;br /&gt;                (&amp;gt;= ny 0)&lt;br /&gt;                (&amp;lt; ny *size*)&lt;br /&gt;                (is-empty (get-p nx ny)))&lt;br /&gt;           (funcall f nx ny))))))&lt;br /&gt;&lt;br /&gt;(&lt;span class="keyword"&gt;defmacro&lt;/span&gt; &lt;span class="function-name"&gt;each-move-from&lt;/span&gt; (x y (nx ny) body)&lt;br /&gt;  `(%each-move-from ,x ,y (&lt;span class="keyword"&gt;lambda&lt;/span&gt; (,nx ,ny) ,body)))&lt;br /&gt;&lt;br /&gt;&lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;display all moves from this position&lt;br /&gt;&lt;/span&gt;(each-square-for-side&lt;br /&gt; (x y p)&lt;br /&gt; (each-move-from&lt;br /&gt;  x y&lt;br /&gt;  (nx ny)&lt;br /&gt;  (print (list x y nx ny p))))&lt;br /&gt;&lt;br /&gt;&lt;span class="comment-delimiter"&gt;;; &lt;/span&gt;&lt;span class="comment"&gt;display all jumps from this position&lt;br /&gt;&lt;/span&gt;(each-square-for-side&lt;br /&gt; (x y p)&lt;br /&gt; (each-jump-from&lt;br /&gt;  x y&lt;br /&gt;  (mx my nx ny)&lt;br /&gt;  (print (list x y mx my nx ny p))))&lt;/pre&gt;&lt;/blockquote&gt;</content><link rel='alternate' type='text/html' href='http://sustainablecode.com/blog/2008/06/checkers-rules-in-lisp.html' title='Checkers Rules in Lisp'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3363235268566529688&amp;postID=8799988181692132758' title='4 Comments'/><link rel='replies' type='application/atom+xml' href='http://sustainablecode.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/8799988181692132758'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/8799988181692132758'/><author><name>Frederick Polgardy</name><uri>http://www.blogger.com/profile/11939486603262164846</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-3363235268566529688.post-6989944862712022203</id><published>2008-05-30T09:44:00.003-05:00</published><updated>2008-06-05T11:15:02.251-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><title type='text'>Lightweight JavaScript Events</title><content type='html'>I've found it handy on a few projects to mix in minimalistic event support to certain JavaScript objects.  The basic idea is that you listen (aka "subscribe") for some event on an object, and get notified by the object when it fires (aka "publishes") that event, along with any parameters it decides to attach.  This is ridiculously easy to implement in JavaScript, yet it's a really powerful abstraction mechanism for UI programming.  It's even easier when you use a library like Prototype to take advantage of its Enumerable and Function extensions.  Here it is in under 20 lines of code:&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="code-block"&gt;EventManager = {&lt;br /&gt; notify: &lt;span class="keyword"&gt;function&lt;/span&gt;() {&lt;br /&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; (this._listeners) {&lt;br /&gt;      &lt;span class="keyword"&gt;var&lt;/span&gt; &lt;span class="variable-name"&gt;args&lt;/span&gt; = $A(arguments), event = args.shift();&lt;br /&gt;      this._listeners.each(&lt;span class="keyword"&gt;function&lt;/span&gt;(listener) {&lt;br /&gt;          listener[event] &amp;amp;&amp;amp; &lt;span class="keyword"&gt;function&lt;/span&gt;() {&lt;br /&gt;            listener[event].apply(&lt;span class="keyword"&gt;this&lt;/span&gt;, args);&lt;br /&gt;          }.defer();&lt;br /&gt;        }, &lt;span class="keyword"&gt;this&lt;/span&gt;);&lt;br /&gt;    }&lt;br /&gt;  },&lt;br /&gt; &lt;br /&gt; listen: &lt;span class="keyword"&gt;function&lt;/span&gt;(listener) {&lt;br /&gt;    this._listeners = this._listeners || [];&lt;br /&gt;    this._listeners.push(listener);&lt;br /&gt;  }&lt;br /&gt;};&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;To add event support to a class, just mixin the EventManager extension:&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="code-block"&gt;Foo = Class.create({&lt;br /&gt;  setX: &lt;span class="keyword"&gt;function&lt;/span&gt;(x) {&lt;br /&gt;      this.x = x;&lt;br /&gt;      this.notify(&lt;span class="string"&gt;"setX"&lt;/span&gt;, this.x);&lt;br /&gt;    }&lt;br /&gt;  });&lt;br /&gt;Foo.addMethods(EventManager);&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;And then get down to it:&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="code-block"&gt;&lt;span class="keyword"&gt;var&lt;/span&gt; &lt;span class="variable-name"&gt;object&lt;/span&gt; = &lt;span class="keyword"&gt;new&lt;/span&gt; Foo();&lt;br /&gt;object.listen({&lt;br /&gt;  setX: &lt;span class="keyword"&gt;function&lt;/span&gt;(x) {&lt;br /&gt;      alert(&lt;span class="string"&gt;"x was initialized to "&lt;/span&gt; + x);&lt;br /&gt;    }&lt;br /&gt;  });&lt;br /&gt;object.setX(1);&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;That's about it.  Let me know if it comes in handy.</content><link rel='alternate' type='text/html' href='http://sustainablecode.com/blog/2008/05/lightweight-javascript-events.html' title='Lightweight JavaScript Events'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3363235268566529688&amp;postID=6989944862712022203' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://sustainablecode.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/6989944862712022203'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/6989944862712022203'/><author><name>Frederick Polgardy</name><uri>http://www.blogger.com/profile/11939486603262164846</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-3363235268566529688.post-641676094223079285</id><published>2008-05-20T21:08:00.004-05:00</published><updated>2008-05-21T07:48:37.005-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><category scheme='http://www.blogger.com/atom/ns#' term='abstraction'/><title type='text'>Lisp, Agility, Readability</title><content type='html'>I recently had the opportunity to witness a curious dialogue on the mailing list for a local user group I participate in.  The original poster, a public devotee and evangelist for Agile development practices, posted an announcement to the group for an upcoming Lisp workshop.  A responder immediately threw down the gauntlet – How can you possibly be advocating Lisp as a development language, when Agile is so much about code readability?  Doesn't Lisp encourage some of the most unreadable code on the planet?  Doesn't Lisp stand for “Lots of Irritating Superfluous Parentheses?”  Doesn't Lisp ... ?&lt;br /&gt;&lt;br /&gt;I thought about this challenge for a minute, and had an immediate sense of both the rightness and wrongness of it.  After all, haven't we all beaten our heads against the wall for days trying to debug some Rails code, only to realize that the answer was buried deep in some “magic” meta-programming hook?  Or spent the afternoon  hunting down the documentation for some obscure method that's throwing an annoying runtime exception, only to discover that our framework kindly generated it for us at compile time based on some esoteric configuration parameter?  Lisp programs seem to represent the pinnacle of this kind of self-generating, self-manipulating metacode, and they can be damned hard to wrap your head around.&lt;br /&gt;&lt;br /&gt;At the same time, isn't it hard to beat that sublime feeling of joy you experience when you write that minimal amount of declarative code, and everything Just Works?  When you can alter some piece of metacode at the global level to effect an entire architectural change, without touching a single line of code throughout your entire application?  Surely there is something deeply right about these kinds of abstractions that allow us to communicate at the level of meaning, as opposed to mechanics – to separate the why of a piece of software from the how.&lt;br /&gt;&lt;br /&gt;It struck me then that we operate under two very different senses of the word “readability,” often slipping and sliding back and forth between the two without realizing it.  To caricature them down to their essence, allow me to call them mechanical readability, and semantic or intentional readability.  &lt;span style="font-style: italic;"&gt;Mechanical&lt;/span&gt; readability is achieved when a piece of code clearly communicates what it &lt;span style="font-style: italic;"&gt;does&lt;/span&gt;, whereas &lt;span style="font-style: italic;"&gt;semantic&lt;/span&gt; or &lt;span style="font-style: italic;"&gt;intentional&lt;/span&gt; readability is achieved when a piece of code clearly communicates what it &lt;span style="font-style: italic;"&gt;means&lt;/span&gt;.  And there's the rub – clearly these two concerns are constraining us constantly, tugging at each other from opposite directions, nearly always forcing us to sacrifice one for the other in any given instance.  What's the answer?  Can they be harmonized into some greater concern that preserves them both?&lt;br /&gt;&lt;br /&gt;I don't think they can be harmonized exactly, but I do think there is a bit of sound earned wisdom that applies here – a jewel in the crown of the software developer's craft.  Without trying to be too scientific or dogmatic about it, it might go something like this:&lt;br /&gt;&lt;blockquote&gt;&lt;ul&gt;&lt;li&gt;When addressing lower layers of abstraction, strive for mechanical readability.&lt;/li&gt;&lt;li&gt;When addressing higher layers of abstraction, strive for semantic readability.&lt;/li&gt;&lt;li&gt;Within a given layer of abstraction, strive to decouple the mechanical and intentional concerns as much as possible.&lt;/li&gt;&lt;/ul&gt;&lt;/blockquote&gt;So where does that leave us as far as Lisp's alleged agility?  Well, in truth, Lisp actually does address mechanical concerns far better than you might think.  But its ability to express the semantic level of software is where it clearly shines.  The temptation is to become obsessed with the semantic approach, to the point of obfuscation of the plain, simple mechanical tasks your software needs to perform.  This is possibly the greatest temptation programmers face when given powerful meta-programming facilities.  There's nothing quite so frustrating as having to work within forced abstractions.&lt;br /&gt;&lt;br /&gt;However, don't assume a more “straightforward” mechanical style will clarify your code either.  The reason your DSL doesn't flow well enough, or map onto your domain well enough, might not be that it's too abstract.  It might just be the wrong abstraction.  The semantic realm is a very conceptual, philosophical sort of beast.  You won't write better abstractions by learning more about technological wizardry, but by practicing the dark art of simple, clear thinking.&lt;br /&gt;&lt;br /&gt;Ultimately, it's the right ideas that set really great software apart.</content><link rel='alternate' type='text/html' href='http://sustainablecode.com/blog/2008/05/lisp-agility-and-abstraction.html' title='Lisp, Agility, Readability'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3363235268566529688&amp;postID=641676094223079285' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://sustainablecode.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/641676094223079285'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/641676094223079285'/><author><name>Frederick Polgardy</name><uri>http://www.blogger.com/profile/11939486603262164846</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-3363235268566529688.post-3012417318721341740</id><published>2008-05-15T12:06:00.006-05:00</published><updated>2008-06-05T11:17:58.623-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='presentations'/><title type='text'>JS Library Roundup</title><content type='html'>In Part III of the JavaScript Renaissance, we spent another long evening exploring &lt;a href="http://prototypejs.org/"&gt;Prototype&lt;/a&gt;, &lt;a href="http://script.aculo.us/"&gt;Script.aculo.us&lt;/a&gt;, and &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt;, and getting a feel for the scope and power of these popular DHTML/Ajax libraries.  A good chunk of that time was spent hacking at the Firebug console and experimenting with live HTML documents for immediate feedback.&lt;br /&gt;&lt;br /&gt;You can download the presentation &lt;a href="http://sustainablecode.com/presentations/JavaScript%20Renaissance%203.ppt"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Thanks again to the Wheaton ex-Perl Mongers for coming out to learn a little more JavaScript.  Come out and see the Perl Mongers reborn as the first arm of the new &lt;a href="http://polyglotprogrammers.com/"&gt;Polyglot Programmers&lt;/a&gt; group!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Addendum&lt;/span&gt;: Some of you at the presentation wanted to know to use Greasemonkey to import libraries into web pages after they've already been loaded.  Here's the skinny:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Go to &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/748"&gt;https://addons.mozilla.org/en-US/firefox/addon/748&lt;/a&gt; and add the Greasemonkey Firefox extension&lt;/li&gt;&lt;li&gt;After you restart Firefox you should see the telltale monkey face icon down in the lower right corner of your browser window&lt;/li&gt;&lt;li&gt;Right click on the monkey face and select "New User Script..."&lt;/li&gt;&lt;li&gt;Give the new script an identifiable name like "load_prototype" or "load_jquery", a namespace (can be anything, I'm not entirely sure what it's even used for), and a list of URLs to include or exclude from running the script&lt;/li&gt;&lt;li&gt;Configure a text editor to edit Greasemonkey scripts&lt;/li&gt;&lt;li&gt;Finally, enter and save the script&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Here's the script I used to load prototype and most of script.aculo.us.  Obviously, you'll need to change the paths to point to the files where they're installed on your system.  Paste this into your new Greasemonkey document below the comments:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code-block"&gt;window.addEventListener(&lt;span class="string"&gt;'load'&lt;/span&gt;, &lt;span class="keyword"&gt;function&lt;/span&gt;() {&lt;br /&gt;    &lt;span class="keyword"&gt;var&lt;/span&gt; &lt;span class="variable-name"&gt;head&lt;/span&gt; = document.getElementsByTagName(&lt;span class="string"&gt;'head'&lt;/span&gt;)[0];&lt;br /&gt;    &lt;span class="keyword"&gt;var&lt;/span&gt; &lt;span class="variable-name"&gt;libs&lt;/span&gt; =&lt;br /&gt;      [&lt;br /&gt;       &lt;span class="string"&gt;'file:///..../prototype.js'&lt;/span&gt;,&lt;br /&gt;       &lt;span class="string"&gt;'file:///..../effects.js'&lt;/span&gt;,&lt;br /&gt;       &lt;span class="string"&gt;'file:///..../dragdrop.js'&lt;/span&gt;,&lt;br /&gt;       &lt;span class="string"&gt;'file:///..../builder.js'&lt;/span&gt;&lt;br /&gt;       ];&lt;br /&gt;    &lt;span class="keyword"&gt;for&lt;/span&gt; (&lt;span class="keyword"&gt;var&lt;/span&gt; &lt;span class="variable-name"&gt;i&lt;/span&gt; &lt;span class="keyword"&gt;in&lt;/span&gt; libs) {&lt;br /&gt;      &lt;span class="keyword"&gt;var&lt;/span&gt; &lt;span class="variable-name"&gt;script&lt;/span&gt; = document.createElement(&lt;span class="string"&gt;'script'&lt;/span&gt;);&lt;br /&gt;      script.type = &lt;span class="string"&gt;"text/javascript"&lt;/span&gt;;&lt;br /&gt;      script.src = libs[i];&lt;br /&gt;      head.appendChild(script);&lt;br /&gt;    }&lt;br /&gt;  }, &lt;span class="keyword"&gt;false&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That should be enough to get you started!  Please hit me up if you have any questions.</content><link rel='alternate' type='text/html' href='http://sustainablecode.com/blog/2008/05/js-library-roundup.html' title='JS Library Roundup'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3363235268566529688&amp;postID=3012417318721341740' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://sustainablecode.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/3012417318721341740'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/3012417318721341740'/><author><name>Frederick Polgardy</name><uri>http://www.blogger.com/profile/11939486603262164846</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-3363235268566529688.post-2198623051891974588</id><published>2008-02-17T14:58:00.006-06:00</published><updated>2008-06-05T11:20:21.864-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><title type='text'>Function Arity and JavaScript</title><content type='html'>A commenter on my last post asked about variable arity methods in JavaScript.  Arity, for those unfamiliar with that term, simply refers to the number of parameters a function takes.  In dynamic languages, however, arity is a more, well, dynamic concept, since a function can often be called with any number of parameters, or even with a collection to be taken as the argument list.  So there are actually two different things going on, which I'll call invocation arity and declaration arity.&lt;br /&gt;&lt;br /&gt;There's not much to invocation arity in JavaScript; you've probably seen it hundreds of times before.  To determine the arity of invocation from within a method, just use the &lt;code&gt;length&lt;/code&gt; property of the array-like &lt;code&gt;arguments&lt;/code&gt; object:&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="code-block"&gt;SomeType.prototype.foo = &lt;span class="keyword"&gt;function&lt;/span&gt; () {&lt;br /&gt;  &lt;span class="keyword"&gt;if&lt;/span&gt; (arguments.length &amp;gt; 2) {&lt;br /&gt;    &lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;do something requiring &amp;gt; 2 arguments&lt;br /&gt;&lt;/span&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Declaration arity is similar: I can ask for the &lt;code&gt;length&lt;/code&gt; property of the function itself.  So for example, if I was passed a function into some higher-order operation, I might check the function's arity to determine how to invoke it:&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="code-block"&gt;&lt;span class="keyword"&gt;function&lt;/span&gt; &lt;span class="function-name"&gt;foreach&lt;/span&gt;(array, callback) {&lt;br /&gt;  &lt;span class="keyword"&gt;for&lt;/span&gt; (&lt;span class="keyword"&gt;var&lt;/span&gt; &lt;span class="variable-name"&gt;i&lt;/span&gt; = 0; i &amp;lt; array.length; ++i) {&lt;br /&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; (callback.length === 1) {&lt;br /&gt;      &lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;yield the element&lt;br /&gt;&lt;/span&gt;      callback(array[i]);&lt;br /&gt;    } &lt;span class="keyword"&gt;else&lt;/span&gt; {&lt;br /&gt;      &lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;yield the index and the element&lt;br /&gt;&lt;/span&gt;      callback(i, array[i]);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Beware declaration arity though: if your function is trying to be smart about its own arity by foregoing formal parameters and just using its &lt;code&gt;arguments&lt;/code&gt; object, it will advertise its arity as zero:&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="code-block"&gt;&lt;span class="keyword"&gt;function&lt;/span&gt; &lt;span class="function-name"&gt;sum&lt;/span&gt;() {&lt;br /&gt;  &lt;span class="keyword"&gt;var&lt;/span&gt; &lt;span class="variable-name"&gt;s&lt;/span&gt; = 0;&lt;br /&gt;  &lt;span class="keyword"&gt;for&lt;/span&gt; (&lt;span class="keyword"&gt;var&lt;/span&gt; &lt;span class="variable-name"&gt;i&lt;/span&gt; = 0; i &amp;lt; arguments.length; ++i) {&lt;br /&gt;    s += arguments[i];&lt;br /&gt;  }&lt;br /&gt;  &lt;span class="keyword"&gt;return&lt;/span&gt; s;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;print(sum.length); &lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;prints 0&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/blockquote&gt;</content><link rel='alternate' type='text/html' href='http://sustainablecode.com/blog/2008/02/function-arity-and-javascript.html' title='Function Arity and JavaScript'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3363235268566529688&amp;postID=2198623051891974588' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://sustainablecode.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/2198623051891974588'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/2198623051891974588'/><author><name>Frederick Polgardy</name><uri>http://www.blogger.com/profile/11939486603262164846</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-3363235268566529688.post-2841253980483842208</id><published>2008-02-14T08:28:00.003-06:00</published><updated>2008-02-14T08:48:39.688-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='presentations'/><title type='text'>More Mongering</title><content type='html'>The &lt;a href="http://chicago.pm.org/"&gt;Perl Mongers&lt;/a&gt; were at it again on Tuesday night, going above and beyond the call of duty, slogging through snow and slush to listen to more JavaScript tidbits.  This time, we explored how to use JavaScript to interact with the browser window and HTML document, monitor and handle user events, and make remote HTTP calls.  We finished up with some lively discussion questions about JavaScript libraries and frameworks and how they compete, relate, and stack on top of one another; so I'll try to go more into that question in the near future.&lt;br /&gt;&lt;br /&gt;But for now, you can find the slides and samples of the presentation &lt;a href="http://sustainablecode.com/presentations/js2.zip"&gt;here&lt;/a&gt;.</content><link rel='alternate' type='text/html' href='http://sustainablecode.com/blog/2008/02/more-mongering.html' title='More Mongering'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3363235268566529688&amp;postID=2841253980483842208' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://sustainablecode.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/2841253980483842208'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/2841253980483842208'/><author><name>Frederick Polgardy</name><uri>http://www.blogger.com/profile/11939486603262164846</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-3363235268566529688.post-3797899435544152261</id><published>2008-02-14T07:43:00.002-06:00</published><updated>2008-02-14T08:23:25.944-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fonts'/><category scheme='http://www.blogger.com/atom/ns#' term='eye candy'/><category scheme='http://www.blogger.com/atom/ns#' term='user interface'/><title type='text'>Eye Candy</title><content type='html'>As good fate would have it, I happened to stumble upon two really good Microsoft fonts yesterday -- Corbel and Consolas.   &lt;a href="http://en.wikipedia.org/wiki/Corbel_%28typeface%29"&gt;Corbel&lt;/a&gt; is a clean proportional-width font, similar in some ways to &lt;a href="http://en.wikipedia.org/wiki/Trebuchet_%28typeface%29"&gt;Trebuchet&lt;/a&gt;, but a little more professional looking in my opinion.  (The loopiness of Trebuchet is a little annoying, and the capital M always bothered me.  Don't ask.) &lt;a href="http://en.wikipedia.org/wiki/Consolas_%28typeface%29"&gt;Consolas&lt;/a&gt; is a fixed-width font, almost entirely sans-serif like Lucida Console, except for a more Courier-looking lowercase g, i, and l.  I've been satisfied with Lucida Console for programming, but it just falls a little bit short of great.  But I think I've finally found a worthy successor.&lt;br /&gt;&lt;br /&gt;By the way, don't even bother trying to run these without ClearType.  You have been warned.&lt;br /&gt;&lt;br /&gt;Unfortunately, these fonts aren't "free" (as in freedom), so I can't just post them for you to download.  But they do come as a &lt;a href="http://www.microsoft.com/downloads/details.aspx?familyid=22e69ae4-7e40-4807-8a86-b3d36fab68d3&amp;amp;displaylang=en"&gt;font pack for Visual Studio 2005&lt;/a&gt; (if you have it installed) -- or for a lower-tech option, a little birdie told me that they come bundled with &lt;a href="http://www.microsoft.com/downloads/details.aspx?familyid=048DC840-14E1-467D-8DCA-19D2A8FD7485&amp;amp;displaylang=en"&gt;PowerPoint Viewer 2007&lt;/a&gt;.</content><link rel='alternate' type='text/html' href='http://sustainablecode.com/blog/2008/02/eye-candy.html' title='Eye Candy'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3363235268566529688&amp;postID=3797899435544152261' title='2 Comments'/><link rel='replies' type='application/atom+xml' href='http://sustainablecode.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/3797899435544152261'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/3797899435544152261'/><author><name>Frederick Polgardy</name><uri>http://www.blogger.com/profile/11939486603262164846</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-3363235268566529688.post-3458537098860638783</id><published>2008-02-01T13:10:00.002-06:00</published><updated>2008-06-05T11:23:31.501-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><title type='text'>Currying in Prototype</title><content type='html'>If you've spent much time in the functional or higher-order programming mindset, you've already gotten a feel for the expressive power that comes from the ability to compose new functions out of existing ones.  In pure functional languages, one of the basic types of function composition operations is called "currying" (after Haskell Curry, an early pioneer in this particular space).  When you curry a function, you essentially bind parameter values to it, and return a new function over the remaining parameters.  Some languages have built-in support for currying as a fundamental operation (ML and Haskell, for example), but you can implement them in any language that supports closures and functions as first-class objects.  The Prototype library, as of version 1.6, does this for JavaScript via the &lt;code&gt;Function.prototype.curry&lt;/code&gt; method.&lt;br /&gt;&lt;br /&gt;Here it is, in all its beautiful compactness (without the &lt;code&gt;Object.extend&lt;/code&gt; confusion):&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="code-block"&gt;Function.prototype.curry = &lt;span class="keyword"&gt;function&lt;/span&gt;() {&lt;br /&gt;  &lt;span class="keyword"&gt;if&lt;/span&gt; (!arguments.length) &lt;span class="keyword"&gt;return&lt;/span&gt; this;&lt;br /&gt;  &lt;span class="keyword"&gt;var&lt;/span&gt; &lt;span class="variable-name"&gt;__method&lt;/span&gt; = &lt;span class="keyword"&gt;this&lt;/span&gt;, args = $A(arguments);&lt;br /&gt;  &lt;span class="keyword"&gt;return&lt;/span&gt; function() {&lt;br /&gt;    &lt;span class="keyword"&gt;return&lt;/span&gt; __method.apply(&lt;span class="keyword"&gt;this&lt;/span&gt;, args.concat($A(arguments)));&lt;br /&gt;  }&lt;br /&gt;};&lt;/pre&gt;&lt;/blockquote&gt;We'll walk through line by line:&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="code-block"&gt;&lt;span class="keyword"&gt;if&lt;/span&gt; (!arguments.length) &lt;span class="keyword"&gt;return&lt;/span&gt; this;&lt;/pre&gt;&lt;/blockquote&gt;If no arguments were bound, simply return the function as-is.&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="code-block"&gt;&lt;span class="keyword"&gt;var&lt;/span&gt; &lt;span class="variable-name"&gt;__method&lt;/span&gt; = &lt;span class="keyword"&gt;this&lt;/span&gt;, args = $A(arguments);&lt;/pre&gt;&lt;/blockquote&gt;Store the &lt;em&gt;original&lt;/em&gt; function in a closure as &lt;code&gt;__method&lt;/code&gt;, and the arguments to be bound as &lt;code&gt;args&lt;/code&gt;.&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="code-block"&gt;&lt;span class="keyword"&gt;return&lt;/span&gt; function() { ... }&lt;/pre&gt;&lt;/blockquote&gt;Nothing surprising here.  We're going to return a new function.&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="code-block"&gt;&lt;span class="keyword"&gt;return&lt;/span&gt; __method.apply(&lt;span class="keyword"&gt;this&lt;/span&gt;, args.concat($A(arguments)));&lt;/pre&gt;&lt;/blockquote&gt;This is where the magic happens.  The newly created function, &lt;em&gt;when called&lt;/em&gt;, is going to return the result of calling the &lt;em&gt;original&lt;/em&gt; function -- &lt;code&gt;__method.apply(this, ...)&lt;/code&gt; -- with the &lt;em&gt;original&lt;/em&gt; arguments -- &lt;code&gt;args&lt;/code&gt; -- prepended to the &lt;em&gt;invocation&lt;/em&gt; arguments.&lt;br /&gt;&lt;br /&gt;An illustration is worth a thousand words:&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="code-block"&gt;&lt;span class="keyword"&gt;function&lt;/span&gt; &lt;span class="function-name"&gt;original&lt;/span&gt;(x, y, z) {&lt;br /&gt;  &lt;span class="keyword"&gt;return&lt;/span&gt; x+y+z;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;var&lt;/span&gt; &lt;span class="variable-name"&gt;bound_x&lt;/span&gt; = original.curry(10);&lt;br /&gt;bound_x(20, 30) &lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;==&amp;gt; original(10, 20, 30)&lt;br /&gt;&lt;/span&gt;                &lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;==&amp;gt; 60&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;sure, you can curry a curried function&lt;br /&gt;&lt;/span&gt;&lt;span class="keyword"&gt;var&lt;/span&gt; &lt;span class="variable-name"&gt;bound_x_y&lt;/span&gt; = bound_x.curry(20);&lt;br /&gt;bound_x_y(30) &lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;==&amp;gt; bound_x(20, 30)&lt;br /&gt;&lt;/span&gt;              &lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;==&amp;gt; original(10, 20, 30)&lt;br /&gt;&lt;/span&gt;              &lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;==&amp;gt; 60&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="keyword"&gt;var&lt;/span&gt; &lt;span class="variable-name"&gt;bound_x&lt;/span&gt; = original.bind(&lt;span class="keyword"&gt;this&lt;/span&gt;, 10);&lt;br /&gt;&lt;span class="keyword"&gt;var&lt;/span&gt; &lt;span class="variable-name"&gt;bound_x_y&lt;/span&gt; = bound_x.bind(&lt;span class="keyword"&gt;this&lt;/span&gt;, 20);&lt;/pre&gt;&lt;/blockquote&gt;</content><link rel='alternate' type='text/html' href='http://sustainablecode.com/blog/2008/02/currying-in-prototype.html' title='Currying in Prototype'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3363235268566529688&amp;postID=3458537098860638783' title='1 Comments'/><link rel='replies' type='application/atom+xml' href='http://sustainablecode.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/3458537098860638783'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/3458537098860638783'/><author><name>Frederick Polgardy</name><uri>http://www.blogger.com/profile/11939486603262164846</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-3363235268566529688.post-7749429016731904414</id><published>2008-01-11T16:28:00.001-06:00</published><updated>2008-06-05T11:25:30.901-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='ajax'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>Testing Ajax calls in Prototype</title><content type='html'>If you're doing any kind of unit testing of Ajax behavior, you'll most likely come upon one minor annoyance.  If you test your UI expectations in the context that &lt;em&gt;fires the request&lt;/em&gt;, the callback won't have completed yet, and the elements you're setting or creating won't be set up.  On the other hand, if you test your expectation in the context that &lt;em&gt;receives the response&lt;/em&gt;, the test runner thread will have ended long ago, and any failures won't be caught and reported.&lt;br /&gt;&lt;br /&gt;What you really want to do is disable asynchronous callback behavior for your test runs, but unfortunately there is no global configuration parameter for Prototype that controls that.&lt;br /&gt;&lt;br /&gt;A little metaprogramming to the rescue.&lt;br /&gt;&lt;br /&gt;Simply add the following to the top of your JSSpec test runner to override asynchronous behavior for your tests:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="code-block"&gt;(&lt;span class="keyword"&gt;function&lt;/span&gt; () {&lt;br /&gt;  &lt;span class="keyword"&gt;var&lt;/span&gt; &lt;span class="variable-name"&gt;$$setOptions&lt;/span&gt; = Ajax.Base.prototype.setOptions;&lt;br /&gt;  Ajax.Base.prototype.setOptions = &lt;span class="keyword"&gt;function&lt;/span&gt; (options) {&lt;br /&gt;    $$setOptions.call(&lt;span class="keyword"&gt;this&lt;/span&gt;, options);&lt;br /&gt;    this.options.asynchronous = &lt;span class="keyword"&gt;false&lt;/span&gt;;&lt;br /&gt;  };&lt;br /&gt; })();&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;(&lt;code&gt;s/setOptions/initialize/g&lt;/code&gt; if you're using the latest and greatest Prototype 1.6.)&lt;br /&gt;&lt;br /&gt;Just fire the events that cause Ajax requests and test your expectations afterward.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;One more thing:&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;If your Ajax response contains &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; elements, Prototype will kindly extract and execute them for you, provided that you pass &lt;code&gt;evalScripts: true&lt;/code&gt; as one of your Ajax options.  However, what you may not expect is that it executes them in a deferred callback using the browser's &lt;code&gt;setTimeout&lt;/code&gt; call.  I initially set out to patch &lt;code&gt;Element.Methods.update&lt;/code&gt;, which handles the deferred evaluation behavior, in much the same way as I did &lt;code&gt;setOptions&lt;/code&gt;, when it occurred to me that a more general solution would be more helpful for testing.  Why not just patch &lt;code&gt;setTimeout&lt;/code&gt; and &lt;code&gt;setInterval&lt;/code&gt; so that they behave synchronously?&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="code-block"&gt;window.setTimeout = window.setInterval = &lt;span class="keyword"&gt;function&lt;/span&gt; (f, timeout) {&lt;br /&gt;  &lt;span class="keyword"&gt;typeof&lt;/span&gt; f === &lt;span class="string"&gt;'string'&lt;/span&gt;? eval(f): f();&lt;br /&gt;}&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;(Again, do this in your test harness, not in your production code!)&lt;br /&gt;&lt;br /&gt;Obviously, you're going to completely lose your desired timing behaviors, but if you're trying to test elaborate timing schemes in JSSpec or JSUnit, you're pretty much screwed anyway.  But let me know if you find any neat tricks to accomplish that.</content><link rel='alternate' type='text/html' href='http://sustainablecode.com/blog/2008/01/testing-ajax-calls-in-prototype.html' title='Testing Ajax calls in Prototype'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3363235268566529688&amp;postID=7749429016731904414' title='2 Comments'/><link rel='replies' type='application/atom+xml' href='http://sustainablecode.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/7749429016731904414'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/7749429016731904414'/><author><name>Frederick Polgardy</name><uri>http://www.blogger.com/profile/11939486603262164846</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-3363235268566529688.post-6918903539241282431</id><published>2008-01-09T15:02:00.000-06:00</published><updated>2008-01-11T08:20:05.568-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='presentations'/><title type='text'>Advanced JavaScript: Not for the Faint of Heart</title><content type='html'>Thanks to the kind folks from &lt;a href="http://chicago.pm.org/"&gt;Chicago Perl Mongers&lt;/a&gt;, who took two hours out of their busy lives last night to learn a little bit about the dark corners and esoterica of JavaScript.&lt;br /&gt;&lt;br /&gt;Those who have only been exposed to procedural and class-based languages often find JavaScript's unique brand of functional programming rather unusual, if not downright confusing.  Its lexical scoping and prototype chaining rules allow for some very powerful metaprogramming facilities; but the language is finicky, and the very features that provide so much power can also be badly abused and hacked.  It seems fair to say that this has been the rule, rather than the exception, until the dawn of the Ajax revolution in 2005.  Did you know that you can easily create arbitrarily deep inheritance hierarchies in JavaScript, even though the language has no concept of classes?  Create higher-order functions that can bind or curry parameters to existing functions?  Extend language and DOM data types with custom functionality?&lt;br /&gt;&lt;br /&gt;The future seems a lot brighter now, thanks to the tireless work of folks like &lt;a href="http://javascript.crockford.com/"&gt;Douglas Crockford&lt;/a&gt;, &lt;a href="http://dean.edwards.name/"&gt;Dean Edwards&lt;/a&gt;, and &lt;a href="http://ejohn.org/"&gt;John Resig&lt;/a&gt;, and to cross-browser JavaScript libraries like &lt;a href="http://prototypejs.org/"&gt;Prototype&lt;/a&gt;, &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt;, and &lt;a href="http://code.google.com/p/base2/"&gt;Base&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The JavaScript Renaissance, Part I: The Core Language&lt;/span&gt;&lt;br /&gt;&lt;a href="http://sustainablecode.com/presentations/JavaScript%20Renaissance%201.ppt"&gt;Presentation&lt;/a&gt;&lt;br /&gt;&lt;a href="http://sustainablecode.com/presentations/Snippets%201.zip"&gt;Snippets&lt;/a&gt;</content><link rel='alternate' type='text/html' href='http://sustainablecode.com/blog/2008/01/advanced-javascript-not-for-faint-of.html' title='Advanced JavaScript: Not for the Faint of Heart'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3363235268566529688&amp;postID=6918903539241282431' title='2 Comments'/><link rel='replies' type='application/atom+xml' href='http://sustainablecode.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/6918903539241282431'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/6918903539241282431'/><author><name>Frederick Polgardy</name><uri>http://www.blogger.com/profile/11939486603262164846</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-3363235268566529688.post-7455594038376373420</id><published>2007-12-30T15:16:00.001-06:00</published><updated>2007-12-30T15:16:46.483-06:00</updated><title type='text'>Christmas</title><content type='html'>&lt;span class="entry-title entry-content"&gt;      LED binary clock for Christmas: $22.99.&lt;br /&gt;&lt;br /&gt;Having a wife who can set it for me: Priceless.&lt;/span&gt;</content><link rel='alternate' type='text/html' href='http://sustainablecode.com/blog/2007/12/christmas.html' title='Christmas'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3363235268566529688&amp;postID=7455594038376373420' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://sustainablecode.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/7455594038376373420'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/7455594038376373420'/><author><name>Frederick Polgardy</name><uri>http://www.blogger.com/profile/11939486603262164846</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-3363235268566529688.post-6988298736497510592</id><published>2007-12-21T08:42:00.000-06:00</published><updated>2007-12-21T09:06:30.430-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>JavaScript 1.7 steals shamelessly from Python</title><content type='html'>A colleague shared a &lt;a href="http://planet.gnome.org/"&gt;Planet GNOME&lt;/a&gt; &lt;a href="http://www.beatniksoftware.com/blog/?p=80"&gt;article&lt;/a&gt; with me this morning about using the &lt;span style="text-decoration: underline;"&gt;&lt;/span&gt;&lt;a href="http://www.neilmix.com/2007/02/07/threading-in-javascript-17/"&gt;Thread.js&lt;/a&gt; library to fake threads and write Erlang-style concurrency in JavaScript.  After briefly investigating said library, I must reluctantly admit that I didn't realize how much really cool stuff there is in JavaScript 1.7 -- much of which has been imported from Python: generators, iterators, and array comprehensions.&lt;br /&gt;&lt;br /&gt;I keep hearing people complaining that JavaScript is becoming too much like Python, and I never understood what they were talking about.  Now I do.  I just don't understand what they're complaining about.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://developer.mozilla.org/en/docs/New_in_JavaScript_1.7"&gt;Mozilla Developer Center - New in JavaScript 1.7&lt;/a&gt;</content><link rel='alternate' type='text/html' href='http://sustainablecode.com/blog/2007/12/javascript-17-steals-shamelessly-from.html' title='JavaScript 1.7 steals shamelessly from Python'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3363235268566529688&amp;postID=6988298736497510592' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://sustainablecode.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/6988298736497510592'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/6988298736497510592'/><author><name>Frederick Polgardy</name><uri>http://www.blogger.com/profile/11939486603262164846</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-3363235268566529688.post-3947362763072547833</id><published>2007-10-26T09:39:00.000-05:00</published><updated>2007-10-26T16:22:39.244-05:00</updated><title type='text'>Beautiful Code</title><content type='html'>I just finished reading &lt;a href="http://books.google.com/books?id=gJrmszNHQV4C&amp;amp;ie=ISO-8859-1"&gt;Beautiful Code&lt;/a&gt; on the train this morning. After all the attention to writing algorithms and solving interesting programming problems in a beautiful way, what actually struck me most was the second-to-last chapter, "Code in Motion," by a couple of the developers of the Perforce source-code control system. The chapter is actually a discussion and elaboration of a &lt;a href="http://www.perforce.com/perforce/papers/prettycode.html"&gt;whitepaper&lt;/a&gt; on the Perforce website called "Seven Pillars of Pretty Code." The beauty under discussion here has nothing to do with the elegance of algorithms, or domain models, or API's, though it has powerful &lt;span style="font-style: italic;"&gt;indirect &lt;/span&gt;relationships to all these things. No, this paper is aimed purely at the &lt;span style="font-style: italic;"&gt;stylistic &lt;/span&gt;beauty of source code, and the way it  &lt;span style="font-style: italic;"&gt;visually &lt;/span&gt;communicates its structure and intent. Or, to quote the opening sentence:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-style: italic;"&gt; The essence of pretty code is that one can infer much about the code's structure from a glance, without completely reading it.&lt;/span&gt;&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;The paper doesn't need any additional explanation from me. I only want to point out a couple things that really crystallized for me while I read it.&lt;br /&gt;&lt;br /&gt;First of all, I've always been fairly adamant about keeping code to 80 columns. This is not necessarily because I do a lot of coding in vi or anything, but for me personally, I tend to like to view multiple "panes" of code side-by-side while I'm working. (At least, that's the most persuasive reason I use against co-workers who use the monitor-width argument to defend their heinously long line-writing practices.) I've always intuited that narrow code is easier to mentally "parse" and comprehend, but I'd never before heard it expressed so beautifully:&lt;span style="color: rgb(0, 0, 0);"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;blockquote style="font-style: italic;"&gt;&lt;span style="color: rgb(0, 0, 0);"&gt;...&lt;/span&gt; the left  edge of the code holds the structure and the right side  holds the detail, and big long lines mix zones of structure  and detail, confusing the reader.&lt;/blockquote&gt;&lt;br /&gt;It was difficult not to jump up out of my seat on the train and shout, "Yes! That's it!" Of course, mindlessly making your code narrower in and of itself won't fix structure/detail problems; but long lines are almost guaranteed to confuse the two. I began to think about some of my stylistic quirks, and realized that almost without fail, they are driven by the structure/detail issue, even if the motivation has been a mostly subconscious one. For example: in general, anything in a comma-separated list always goes on its own line unless the &lt;span style="font-style: italic;"&gt;entire list&lt;/span&gt; fits compactly and &lt;span style="font-style: italic;"&gt;readably &lt;/span&gt;on one line. Breaking a list arbitrarily because you've hit the 80-column mark makes it impossible to see at a glance what your parameters are.&lt;br /&gt;&lt;br /&gt;This discussion also segues nicely into the problem of indentation. Since I've been programming in Python for many years, this is an issue I've had a long time to think about. Most people coming to Python for the first time complain that the syntactically significant indentation is the one thing that stands in their way of really liking the language. It is frequently claimed by Python evangelists that code that uses syntactic indentation is actually easier to read than code that uses braces or do/end-type control statements to delineate blocks. I agree with this statement up to a point; but in a larger sense it's really beside the point. The more important point is that we should be going out of our way to eliminate it as much as possible:&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-style: italic;"&gt;Forcibly align the main flow of control down the left side, with  one level of indentation for if/while/for/do/switch statements.  Use break, continue, return, even 'goto' to coerce the code into  left-side alignment.  Rearrange conditionals so that the block  with the quickest exit comes first, and then return (or break,  or continue) so that the other leg can continue at the same   indentation level.&lt;/blockquote&gt;&lt;br /&gt;Of course, it goes without saying, pull out conditional logic into smaller functions and methods wherever appropriate, with their own sensible indentation schemes.&lt;br /&gt;&lt;br /&gt;What I love most about Python indentation isn't that short functions are easier to read; it's that long, deeply indented functions are &lt;span style="font-style: italic;"&gt;impossible &lt;/span&gt;to read, and so you're virtually forced to refactor your code to make it comprehensible.</content><link rel='alternate' type='text/html' href='http://sustainablecode.com/blog/2007/10/beautiful-code.html' title='Beautiful Code'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3363235268566529688&amp;postID=3947362763072547833' title='7 Comments'/><link rel='replies' type='application/atom+xml' href='http://sustainablecode.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/3947362763072547833'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/3947362763072547833'/><author><name>Frederick Polgardy</name><uri>http://www.blogger.com/profile/11939486603262164846</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-3363235268566529688.post-4925146059492175713</id><published>2007-10-25T11:25:00.002-05:00</published><updated>2008-06-05T13:24:47.178-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='ajax'/><category scheme='http://www.blogger.com/atom/ns#' term='books'/><category scheme='http://www.blogger.com/atom/ns#' term='feeds'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><title type='text'>Google Books and Google Feeds</title><content type='html'>I've really been enjoying playing around with &lt;a href="http://books.google.com/"&gt;Google Books&lt;/a&gt; lately. I've wanted to get my (ever-growing) home library into some kind of shared system for years, but the idea of entering hundreds of ISBN's into a spreadsheet just never seemed like how I wanted to spend my time. And even then, how to convert or import that data into something useful?&lt;br /&gt;&lt;br /&gt;The first big win was that, with Google Books, I could perform advanced queries, such as by title and author, much the same way I'd perform an advanced web search. So, for example:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;intitle:"agile web development with rails" inauthor:"hansson"&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Even though the search would be slightly fuzzier than an exact ISBN search, if it meant not having to pull each and every book off the shelf to get the ISBN, I was willing to deal with a few errors and mismatches that I could correct by hand.&lt;br /&gt;&lt;br /&gt;Since I was really itching to see my books show up in the library as soon as possible, I decided to go quick and dirty and write a Python screen-scraper. The basic idea is: for each book in the spreadsheet, submit the search query, then scrape the results looking for an "Add to my library" link. However, in order to do that, I first needed to log in to Google Books, and capture my User-Agent and Cookie headers that associated me to my session and my library. That logic comprises one of the only two interesting parts of the bot:&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="code-block"&gt;&lt;span class="keyword"&gt;import&lt;/span&gt; urllib, urllib2&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;def&lt;/span&gt; &lt;span class="function-name"&gt;getBookSearchRequest&lt;/span&gt;(title, author):&lt;br /&gt;  query = urllib.quote(&lt;br /&gt;      &lt;span class="string"&gt;'intitle:"'&lt;/span&gt;+title+&lt;span class="string"&gt;'" inauthor:"'&lt;/span&gt;+author+&lt;span class="string"&gt;'"'&lt;/span&gt;)&lt;br /&gt;  req = urllib2.Request(&lt;br /&gt;      &lt;span class="string"&gt;'http://books.google.com/books'&lt;/span&gt;+&lt;br /&gt;      &lt;span class="string"&gt;'?as_brr=0'&lt;/span&gt;+ &lt;span class="comment"&gt;#the advanced search flag&lt;br /&gt;&lt;/span&gt;      &lt;span class="string"&gt;'&amp;amp;q='&lt;/span&gt;+query+&lt;br /&gt;      &lt;span class="string"&gt;'&amp;amp;btnG=Search+Books'&lt;/span&gt;)&lt;br /&gt;  req.add_header(&lt;span class="string"&gt;'Host'&lt;/span&gt;, &lt;span class="string"&gt;'books.google.com'&lt;/span&gt;)&lt;br /&gt;  req.add_header(&lt;span class="string"&gt;'User-Agent'&lt;/span&gt;, HARDCODED_USER_AGENT)&lt;br /&gt;  req.add_header(&lt;span class="string"&gt;'Cookie'&lt;/span&gt;, HARDCODED_GOOGLE_COOKIE)&lt;br /&gt;  &lt;span class="keyword"&gt;return&lt;/span&gt; req&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;The other interesting part was scraping for the link:&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="code-block"&gt;&lt;span class="keyword"&gt;import&lt;/span&gt; re, cgi&lt;br /&gt;&lt;br /&gt;&lt;span class="keyword"&gt;for&lt;/span&gt; book &lt;span class="keyword"&gt;in&lt;/span&gt; books:&lt;br /&gt;  page = urllib2.urlopen(&lt;br /&gt;      getBookSearchRequest(&lt;br /&gt;          book.name, book.author)).read()&lt;br /&gt;  match = re.search(&lt;br /&gt;      r&lt;span class="string"&gt;'&amp;lt;a href="([^"]*)"&amp;gt;\s*Add to my library\s*&amp;lt;/a&amp;gt;'&lt;/span&gt;,&lt;br /&gt;      page,&lt;br /&gt;      re.DOTALL)&lt;br /&gt;  &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="keyword"&gt;not&lt;/span&gt; match:&lt;br /&gt;      &lt;span class="keyword"&gt;continue&lt;/span&gt;&lt;br /&gt;  link = match.group(1)&lt;br /&gt;  &lt;span class="comment"&gt;# handle the result...&lt;/span&gt;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;The rest basically boils down to HTTP retry logic and gracefully bailing out when no results are found. Anyway, before I had time to groan and say, "Ugh!" I had about 230 out of 250 books imported, most of which were actually not in the system as requested. Not too shabby.&lt;br /&gt;&lt;br /&gt;Having a library at Google Books also gave me an opportunity to play with the Google Feed JavaScript API. With a simple JavaScript call, you can retrieve data from any RSS or Atom feed, and dynamically inject it into your page with your Ajax library of choice.&lt;br /&gt;&lt;br /&gt;Here is my otherwise empty &lt;a href="http://fred.polgardy.com/"&gt;personal page&lt;/a&gt; with the five books most recently added to my library. And here's the JavaScript code to pull the feed:&lt;br /&gt;&lt;blockquote&gt;&lt;pre class="code-block"&gt;&lt;span class="keyword"&gt;var&lt;/span&gt; &lt;span class="variable-name"&gt;feed&lt;/span&gt; = &lt;span class="keyword"&gt;new&lt;/span&gt; google.feeds.Feed(&lt;br /&gt;  &lt;span class="string"&gt;'http://books.google.com/books?as_list='&lt;/span&gt;+&lt;br /&gt;  &lt;span class="string"&gt;'BDToX1-EQuq7cjr6nqdzfARoU-HJfh-GeA1cvLGf59B-j5Y0JG3Y'&lt;/span&gt;+&lt;br /&gt;  &lt;span class="string"&gt;'&amp;amp;output=rss'&lt;/span&gt;);&lt;br /&gt;feed.setNumEntries(5);&lt;br /&gt;feed.load(&lt;span class="keyword"&gt;function&lt;/span&gt;(result) {&lt;br /&gt;  &lt;span class="keyword"&gt;if&lt;/span&gt; (!result.error) {&lt;br /&gt;      &lt;span class="keyword"&gt;for&lt;/span&gt; (&lt;span class="keyword"&gt;var&lt;/span&gt; &lt;span class="variable-name"&gt;i&lt;/span&gt; = 0; i &amp;lt; result.feed.entries.length; ++i) {&lt;br /&gt;            &lt;span class="comment-delimiter"&gt;// &lt;/span&gt;&lt;span class="comment"&gt;handle the item...&lt;br /&gt;&lt;/span&gt;       }&lt;br /&gt;   }&lt;br /&gt;});&lt;/pre&gt;&lt;/blockquote&gt;</content><link rel='alternate' type='text/html' href='http://sustainablecode.com/blog/2007/10/google-books-and-google-feeds.html' title='Google Books and Google Feeds'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3363235268566529688&amp;postID=4925146059492175713' title='2 Comments'/><link rel='replies' type='application/atom+xml' href='http://sustainablecode.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/4925146059492175713'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/4925146059492175713'/><author><name>Frederick Polgardy</name><uri>http://www.blogger.com/profile/11939486603262164846</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-3363235268566529688.post-1999413999999706739</id><published>2007-06-11T07:34:00.000-05:00</published><updated>2007-06-12T07:57:13.635-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='about'/><title type='text'>About Me</title><content type='html'>This is a short stroll down memory lane, a little bit of history about how I got started, and how I got to be where I am today. Since this is the technology blog, I'll resist the urge to write a shameless autobiography, and give this a more modest tech focus.&lt;br /&gt;&lt;br /&gt;I got my first computer, a &lt;a href="http://en.wikipedia.org/wiki/TI-99"&gt;TI-99/4A&lt;/a&gt;, in 1983, when Texas Instruments started practically giving them away in the price war with Commodore. For awhile, I mostly used it to brush up on my chess skills (which have since fallen into dormancy); but eventually, out of curiosity, I started typing in the sample BASIC programs from the &lt;span style="font-style: italic;"&gt;Beginners' Guide&lt;/span&gt; and &lt;span style="font-style: italic;"&gt;Users' Guide&lt;/span&gt; books, and modifying them in small ways to see what would result. My cassette player, not to mention the upper range of my eardrums, got a serious workout.&lt;br /&gt;&lt;br /&gt;My next purchase was an &lt;a href="http://en.wikipedia.org/wiki/Atari_8-bit_family"&gt;Atari 800XL&lt;/a&gt;, also for $50. By 1987 it was already obsolete, but still more powerful and better supported than the TI. A friend and I procured them at the same time in order to work on projects together remotely, carry on electronic correspondence, and trade games. Over the next few years, my BASIC programming skills had gotten fairly good, and I even managed to do some rudimentary machine code optimizations and display list programming. (A wonderful little feature of the Atari 8-bit family was that the "graphics mode" was actually a program written in a mini instruction set, whereby you could alter screen resolution, swap color and font tables, and even jump away to short assembly subroutines, in between the drawing cycles of your monitor or TV. Scary stuff.)&lt;br /&gt;&lt;br /&gt;Sadly though, that was going to be it for programming for awhile. With both my machines already in the graveyard, and no low-cost computing options on the horizon, I basically assumed I'd had my fun, and it was time to move on. I maintained my enthusiasm for math and science throughout high school, but I became more interested in the arts and in music. I entered the conservatory at Wheaton College a music composition major, and flew the coop four years later with a liberal arts degree in philosophy. (But that's a story for another blog.)&lt;br /&gt;&lt;br /&gt;Fortunately, the Unix-based e-mail machines in the college computer lab rekindled my love of programming just in time, providing a convenient little sandbox for playing around with basic shell scripting, and when I was feeling especially brave, a C compiler. For fun, my final semester I petitioned to take an artificial intelligence independent study course for philosophy credit, during which I wrote a decent checker player in DOS/C. Then in 1995, as I donned my cap and gown, the World Wide Web began to explode with a vengeance, and I was fortunate enough to get in on the ground level.&lt;br /&gt;&lt;br /&gt;After a couple brief admin/web projects over the next two years, I was invited to join an incredible consulting company, founded by two former Motorola engineers, which introduced me to everything from web application architecture to embedded systems development. It was here that I first had any serious mentoring, any actual design meetings or code reviews, and most importantly, the privilege of working with such talented &lt;span style="font-style: italic;"&gt;friends&lt;/span&gt;. This formative six-year period was in every way a giant leap forward for me as a developer. When my interests began to turn to different design and development approaches, I brought my experience to a new team, and have helped them navigate significant platform changes and a host of new product offerings.&lt;br /&gt;&lt;br /&gt;Today, I think of myself as a kind of software philosopher, trying to bring conceptual clarity into the process of developing software systems. I approach software development as a kind of &lt;span style="font-style: italic;"&gt;applied philosophy&lt;/span&gt;: a concrete exercise in analyzing problem domains down to the most appropriate and sensible level of detail, and then dealing with the entities and relationships that arise on their own terms. In my experience, I know of no other way to keep systems from spiraling out of control, and becoming unmanageable. I strive for separation of concerns and readability of code above all, unless there are about twenty pressing reasons not to.&lt;br /&gt;&lt;br /&gt;And, as in all other areas of life, I try not to take it too seriously, and try to maintain an open mind towards what I know, and what I learn.</content><link rel='alternate' type='text/html' href='http://sustainablecode.com/blog/2007/06/about-me.html' title='About Me'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3363235268566529688&amp;postID=1999413999999706739' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://sustainablecode.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/1999413999999706739'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/1999413999999706739'/><author><name>Frederick Polgardy</name><uri>http://www.blogger.com/profile/11939486603262164846</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-3363235268566529688.post-7298181474594298005</id><published>2007-06-08T14:27:00.000-05:00</published><updated>2007-06-08T14:44:15.096-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='infoq'/><title type='text'>InfoQ Top Content</title><content type='html'>There are some really good presentations here. Many are specific, but "How To Design a Good API" is definitely worth an hour of your time.&lt;br /&gt;&lt;br /&gt;#1 &lt;a href="http://cts.vresp.com/c/?InfoQ/a887d6a7f5/06c0bcc163/7aafdb1bfb"&gt;Spring 2.0: What's New and Why it Matters&lt;/a&gt;&lt;br /&gt;#2 &lt;a href="http://cts.vresp.com/c/?InfoQ/a887d6a7f5/06c0bcc163/a4c9c1d692"&gt;A Look at Common Performance Problems in Rails&lt;/a&gt;&lt;br /&gt;#3 &lt;a href="http://cts.vresp.com/c/?InfoQ/a887d6a7f5/06c0bcc163/0b5d27ba47"&gt;How to Design a Good API &amp;amp; Why it Matters&lt;/a&gt;&lt;br /&gt;#4 &lt;a href="http://cts.vresp.com/c/?InfoQ/a887d6a7f5/06c0bcc163/9a9db2b4aa"&gt;The Role of the Enterprise Service Bus&lt;/a&gt;&lt;br /&gt;#5 &lt;a href="http://cts.vresp.com/c/?InfoQ/a887d6a7f5/06c0bcc163/7c64ffa012"&gt;Migrating to Struts 2 - Part II&lt;/a&gt;&lt;br /&gt;#6 &lt;a href="http://cts.vresp.com/c/?InfoQ/a887d6a7f5/06c0bcc163/e6a9e0002f"&gt;Introduction to Domain Specific Languages&lt;/a&gt;&lt;br /&gt;#7 &lt;a href="http://cts.vresp.com/c/?InfoQ/a887d6a7f5/06c0bcc163/cb8b566e49"&gt;Introduction to JBoss Seam&lt;/a&gt;&lt;br /&gt;#8 &lt;a href="http://cts.vresp.com/c/?InfoQ/a887d6a7f5/06c0bcc163/096b301401"&gt;From Java to Ruby: Risk&lt;/a&gt;&lt;br /&gt;#9 &lt;a href="http://cts.vresp.com/c/?InfoQ/a887d6a7f5/06c0bcc163/69a9e69748"&gt;Integrating Java Content Repository and Spring&lt;/a&gt;&lt;br /&gt;#10 &lt;a href="http://cts.vresp.com/c/?InfoQ/a887d6a7f5/06c0bcc163/56579e89b3"&gt;Introduction to BackgrounDRb&lt;/a&gt;&lt;br /&gt;#11 &lt;a href="http://cts.vresp.com/c/?InfoQ/a887d6a7f5/06c0bcc163/3b0eb40107"&gt;David H. Hansson on the Future of Rails&lt;/a&gt;&lt;br /&gt;#12 &lt;a href="http://cts.vresp.com/c/?InfoQ/a887d6a7f5/06c0bcc163/b78be0ee01"&gt;Casestudy: Brasilian National Healthcare System&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.infoq.com/news/2007/06/infoq-birthday"&gt;Rest of article&lt;/a&gt;</content><link rel='alternate' type='text/html' href='http://sustainablecode.com/blog/2007/06/infoq-top-content.html' title='InfoQ Top Content'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3363235268566529688&amp;postID=7298181474594298005' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://sustainablecode.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/7298181474594298005'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/7298181474594298005'/><author><name>Frederick Polgardy</name><uri>http://www.blogger.com/profile/11939486603262164846</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-3363235268566529688.post-8031239477221371491</id><published>2007-05-15T12:55:00.000-05:00</published><updated>2007-06-08T14:44:54.791-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='articles'/><title type='text'>Simple, Easy</title><content type='html'>&lt;blockquote&gt;Simplicity is widely misunderstood (in programming as well as in life in general). It doesn't mean simplistic, amateurish, or insufficient in any way. Quite the opposite. Simplicity is often much more difficult to achieve than an overly complex, kludgey solution.&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: right;"&gt;-- &lt;span style="font-style: italic;"&gt;Practices of an Agile Developer&lt;/span&gt;, Subramaniam and Hunt&lt;br /&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/blockquote&gt;We've all been in this meeting. A new system requirement is put on the table. Business stakeholders and developers join together in a throng of thoughts, ideas, and concerns. At the conclusion of the exchange, someone chimes in: "Look, the important thing is that we just need to keep it &lt;span&gt;simple&lt;/span&gt;, and not over-engineer the solution." Everyone nods their heads and gets down to business, believing that there was substantial agreement on what was said. But confusion is waiting in the wings. Did the summarizer mean &lt;span style="font-style: italic;"&gt;simple&lt;/span&gt; or &lt;span style="font-style: italic;"&gt;easy&lt;/span&gt;? Which version was heard by the participants? What's the difference anyway?&lt;br /&gt;&lt;br /&gt;When concepts become confused in this way, I often find it helpful to distinguish them by means of their opposites. The opposite of &lt;span style="font-style: italic;"&gt;simple&lt;/span&gt; is &lt;span style="font-style: italic;"&gt;complex&lt;/span&gt;, while the opposite of &lt;span style="font-style: italic;"&gt;easy&lt;/span&gt; is &lt;span style="font-style: italic;"&gt;difficult&lt;/span&gt;. Your project's &lt;span style="font-style: italic;"&gt;complexity&lt;/span&gt; is the degree to which it is comprised of mutually interlocking components or hierarchical layers. Your project's &lt;span style="font-style: italic;"&gt;difficulty&lt;/span&gt; is simply a measure of the effort required to develop it.&lt;br /&gt;&lt;br /&gt;Think of complexity as applying both to the conceptual domain (model) of a system, and to its implementation. In the domain arena, the core skills for managing complexity are &lt;span style="font-style: italic;"&gt;analysis&lt;/span&gt; and &lt;span style="font-style: italic;"&gt;design&lt;/span&gt;: breaking down the problem conceptually, and matching the domain of the solution to the domain of the problem. In the implementation arena, the core skill is &lt;span style="font-style: italic;"&gt;development&lt;/span&gt;: expressing the solution to the problem in some kind of programming language, in a way that clearly communicates both the structure &lt;span style="font-style: italic;"&gt;and&lt;/span&gt; the intent of the design. In traditional "waterfall" methodologies, these exercises tended to be distinct and non- (or mostly non-) overlapping. In contemporary "agile" methodologies, they are tightly integrated and iterative.&lt;br /&gt;&lt;br /&gt;At first glance, it would seem that project complexity and project difficulty would have a fairly direct relationship. When your system's complexity is properly understood and managed, this is probably true at a very macroscopic level: don't expect to implement and test a transaction server or remoting system with the same effort it takes to write a little CRUD web application for your corporate intranet. However, if your team isn't making the effort to analyze problem domains, and design solutions that fit, the ease &lt;span style="font-style: italic;"&gt;and &lt;/span&gt;simplicity you're trying to achieve will forever elude you. Ironically, by focusing on the easy-difficult axis instead of the simple-complex axis up front, you will create far more difficulty in the long run.&lt;br /&gt;&lt;br /&gt;Start paying very close attention to the way you use the words &lt;span style="font-style: italic;"&gt;simple&lt;/span&gt; and &lt;span style="font-style: italic;"&gt;easy&lt;/span&gt;, and see if it's not the source of much confusion in your own thinking, and much misunderstanding in your development strategy.&lt;br /&gt;&lt;br /&gt;Common sense? Perhaps. But the temptation lurks just beneath the surface of that all too common mantra: "Keep it simple!" We simply must dismantle this confusion as soon as possible. It won't be easy!&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;&lt;blockquote&gt;"Make everything as simple as possible, but not simpler."&lt;br /&gt;&lt;div style="text-align: right;"&gt;  -- &lt;span style="font-style: italic;"&gt;Albert Einstein&lt;/span&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;/div&gt;</content><link rel='alternate' type='text/html' href='http://sustainablecode.com/blog/2007/05/simple-easy.html' title='Simple, Easy'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3363235268566529688&amp;postID=8031239477221371491' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://sustainablecode.com/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/8031239477221371491'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3363235268566529688/posts/default/8031239477221371491'/><author><name>Frederick Polgardy</name><uri>http://www.blogger.com/profile/11939486603262164846</uri><email>noreply@blogger.com</email></author></entry></feed>