<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="/stylesheets/rss.css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>Aaron Feng: Lisp ninja</title>
    <link>http://aaronfeng.com/articles/2008/04/29/lisp-ninja</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>Adventures in software development</description>
    <item>
      <title>Lisp ninja</title>
      <description>&lt;p&gt;It's not hard to get caught up on cranking out code when you are in the zone.  All of the unit tests are passing one after another, and the implementation just flows out from your finger tips.  There's no need to stop, step back and question the work because everything feels so right.  Situations like this can really hinder your ability to unleash the power of your language.  Especially if Lisp is your language of choice.  After all, Lisp is the official programmable programming language.&lt;/p&gt;

&lt;p&gt;This recently happened to me.  I have been working on a side project (I'll talk about it in later post) that required me to implement string functions in Common Lisp that are similar, in spirit, to ones found in PHP.  Since I'm a huge fan of TDD, I  started with a failing test.  However, before the failing test can be written, I need to confirm the actual behavior in PHP.  During my the implementation cycle, I toggled back and forth between PHP and Lisp.  This can get tedious over time, but I just kept chugging along.&lt;/p&gt;

&lt;p&gt;This morning &lt;a href="http://kyle-burton.livejournal.com/"&gt;Kyle Burton&lt;/a&gt; showed me the light.  He sent me a few pieces of code that remove the tediousness from my implementation cycle.  I adopted the code to fit my environment and the testing framework.  Without further adieu:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;(defparameter *php-bin* &amp;quot;/opt/local/bin/php&amp;quot;)

(when (not (probe-file *php-bin*))
  (error 
   &amp;quot;Error: %s was not found, please update *php-bin* to point to your php.&amp;quot; 
   *php-bin*))

(defun get-result-for-test-case (instr 
                 &amp;amp;optional (width 75) 
                 (lbreak #\Newline) 
                 (cut nil))
  (with-output-to-string (str)
    (run-program *php-bin* (list 
                &amp;quot;-r&amp;quot; 
                (format nil &amp;quot;echo wordwrap(\&amp;quot;~a\&amp;quot;,~a,\&amp;quot;~a\&amp;quot;,~a);&amp;quot;
                    instr
                    width
                    lbreak
                    (if cut &amp;quot;true&amp;quot; &amp;quot;false&amp;quot;)))
         :output str)))

(get-result-for-test-case &amp;quot;aaron feng&amp;quot; 3)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I was executing PHP code externally just to get the output, so I can use it in my unit tests.  Why not ask PHP from Lisp!  &lt;/p&gt;

&lt;p&gt;The last line calls get-result-for-test-case function which calls the PHP wordwrap function directly and returns the result.  Now this removed the need for me having to go outside my Lisp environment while I'm hacking.  Why stop here?  Why not have a macro that generates the test case using the result from PHP?&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;(defmacro create-test-case (test-name string &amp;amp;optional (width 75)
                (lbreak #\Newline) (cut nil))
  `(addtest ,test-name
       (let ((input ,string)
         (expected ,(get-result-for-test-case string width lbreak cut)))
     (ensure-same (wordwrap input ,width ,lbreak ,cut) expected))))

(create-test-case empty-string &amp;quot;   &amp;quot; 4)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Voila, instant test case.  the last line shows how the test case can be generated on the fly and run.  If you are a real ninja, you need to take one more step further.&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_default "&gt;(defmacro create-test-cases (&amp;amp;rest cases)
 `(prog2
      (deftestsuite wordwrap-suite () ())
      ,@(mapcar #'(lambda (case) (macroexpand-1 `(create-test-case
,@case))) cases)))

(create-test-cases
                (empty-string1       &amp;quot;   &amp;quot;)
                (empty-string2       &amp;quot;   &amp;quot; 4)
                (no-wrap             &amp;quot;foo&amp;quot;)
                (wrap-one-word-at-1  &amp;quot;foo&amp;quot; 1))&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;create-test-cases creates a testing suite to host all the test cases then it generates test cases based on input.  This takes WAY LESS typing than hand writing each test case.  I no longer have to switch between two environments (PHP and Lisp) because it's all automatically done for me!  Thanks Kyle!&lt;/p&gt;</description>
      <pubDate>Tue, 29 Apr 2008 22:27:00 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:282ea714-3797-4b72-984d-85a97ce39b8b</guid>
      <author>Aaron Feng</author>
      <link>http://aaronfeng.com/articles/2008/04/29/lisp-ninja</link>
      <category>programming</category>
    </item>
    <item>
      <title>"Lisp ninja" by Aaron Feng</title>
      <description>&lt;p&gt;Leslie,&lt;/p&gt;

&lt;blockquote&gt;
    &lt;p&gt;Did you know that this little gadget by Xach uses Lisp to generate PHP?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No, sounds pretty interesting.&lt;/p&gt;</description>
      <pubDate>Wed, 30 Apr 2008 06:35:08 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:faac16ca-b413-4fef-8e67-2caf47004ea6</guid>
      <link>http://aaronfeng.com/articles/2008/04/29/lisp-ninja#comment-5283</link>
    </item>
    <item>
      <title>"Lisp ninja" by Aaron Feng</title>
      <description>&lt;p&gt;Jonathan,&lt;/p&gt;

&lt;blockquote&gt;
    &lt;p&gt;With slight modification to get-result-for-test-case, it can be generalized for any PHP function&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I also thought about it too.  I'm not sure if the super generic version will out weight the ability to allow Lisp to do error checking for you?  But this should be interesting.&lt;/p&gt;</description>
      <pubDate>Wed, 30 Apr 2008 06:30:54 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:a7f61b92-b5ed-4c2d-b323-c2ca37580294</guid>
      <link>http://aaronfeng.com/articles/2008/04/29/lisp-ninja#comment-5282</link>
    </item>
    <item>
      <title>"Lisp ninja" by Aaron Feng</title>
      <description>&lt;p&gt;Kyle,&lt;/p&gt;

&lt;blockquote&gt;
    &lt;p&gt;Why did you use prog2 in create-test-cases?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Good question.  I originally wanted to return the second form because I added deftestsuite to create-test-cases.  Since there are no side effects, it doesn't actually matter.&lt;/p&gt;</description>
      <pubDate>Wed, 30 Apr 2008 06:27:38 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:037374c7-d1bf-49f0-b9d6-3568f692e278</guid>
      <link>http://aaronfeng.com/articles/2008/04/29/lisp-ninja#comment-5281</link>
    </item>
    <item>
      <title>"Lisp ninja" by Leslie P. Polzer</title>
      <description>&lt;p&gt;Did you know that &lt;a href="http://www.xach.com/scammer-name/"&gt;this little gadget&lt;/a&gt; by Xach uses Lisp to generate PHP?&lt;/p&gt;</description>
      <pubDate>Wed, 30 Apr 2008 01:44:04 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:6fd5f1fa-be66-4857-b08f-6df45b5f4e5f</guid>
      <link>http://aaronfeng.com/articles/2008/04/29/lisp-ninja#comment-5280</link>
    </item>
    <item>
      <title>"Lisp ninja" by Jonathan T</title>
      <description>&lt;p&gt;Why stop there?&lt;/p&gt;

&lt;p&gt;With slight modification to &lt;code&gt;get-result-for-test-case&lt;/code&gt;, it can be generalized for any PHP function, not just &lt;code&gt;wordwrap&lt;/code&gt;, which is especially easy when the Lisp function and the PHP function have the same name.&lt;/p&gt;</description>
      <pubDate>Wed, 30 Apr 2008 01:04:34 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:571a6bfd-ac1d-44af-91c6-8a187ea412d9</guid>
      <link>http://aaronfeng.com/articles/2008/04/29/lisp-ninja#comment-5279</link>
    </item>
    <item>
      <title>"Lisp ninja" by Kyle</title>
      <description>&lt;p&gt;It's not just that it's less typing - you're less likely to make mistakes (reducing the LOC), you're more easily able to see the test case data itself (it's all lined up).  You've created a mini-DSL for testing your code.&lt;/p&gt;

&lt;p&gt;I was on the fence about whether to one-time generate the test cases (and thus not require an installation of php to run them), have the php output be created at compile time (macro expansion time) or leave it up till the time the test case is run.  I guess there are a lot of ways this could have gone...&lt;/p&gt;

&lt;p&gt;Why did you use prog2 in create-test-cases?&lt;/p&gt;</description>
      <pubDate>Wed, 30 Apr 2008 00:06:31 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:11840eaf-20dd-4d43-b03f-7a840a80b386</guid>
      <link>http://aaronfeng.com/articles/2008/04/29/lisp-ninja#comment-5278</link>
    </item>
  </channel>
</rss>
