<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/stylesheets/rss.css" type="text/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>Tom Moertel's Weblog: Tag evaluators</title>
    <link>http://blog.moertel.com/articles/tag/evaluators?tag=evaluators</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>Quality rants on programming theory and stuff geeks like</description>
    <item>
      <title>Writing a simple Ruby evaluator in Haskell</title>
      <description>&lt;p&gt;In the last few days I have been learning
&lt;a href="http://www.ruby-lang.org/"&gt;Ruby&lt;/a&gt;, something I have had on my
to-do list for a long time.  Luckily, I now have a project for which &lt;a href="http://www.rubyonrails.org/"&gt;Ruby on Rails&lt;/a&gt;
is perfect, and so now is the perfect time to get more into Ruby.&lt;/p&gt;


	&lt;p&gt;Naturally, I am making much use of the second edition of &amp;#8220;The Pickaxe,&amp;#8221; (pragmatic) Dave Thomas&amp;#8217;s book
&lt;em&gt;Programming Ruby&lt;/em&gt;  (the &lt;a href="http://www.rubycentral.com/book/"&gt;first edition of which&lt;/a&gt; is available online).  Overall, it is a great book: good organization, lively writing, and superb examples.&lt;/p&gt;


	&lt;p&gt;But I must say I have one source of frustration.  I am a computer-language guy, and I frequently find myself thinking, &lt;em&gt;that&amp;#8217;s great, but what &lt;strong&gt;exactly&lt;/strong&gt; does this mean?&lt;/em&gt;  I&amp;#8217;ll give you an example, which I found quite surprising.&lt;/p&gt;


	&lt;p&gt;Using the following Ruby code, you can create what is in effect your own while-loop construct:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;my_while&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;cond&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
  &lt;span class="keyword"&gt;break&lt;/span&gt; &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="ident"&gt;cond&lt;/span&gt;
  &lt;span class="keyword"&gt;yield&lt;/span&gt;
  &lt;span class="keyword"&gt;retry&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="ident"&gt;i&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;
&lt;span class="ident"&gt;my_while&lt;/span&gt; &lt;span class="ident"&gt;i&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="number"&gt;10&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
  &lt;span class="ident"&gt;print&lt;/span&gt; &lt;span class="ident"&gt;i&lt;/span&gt;
  &lt;span class="ident"&gt;i&lt;/span&gt; &lt;span class="punct"&gt;+=&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;  

&lt;span class="comment"&gt;# output: 0123456789&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;At first, this blew my mind.  Why?  Because while reading the book, I
was building up an operational semantics for the Ruby language in my
head, and my understanding of what it &lt;em&gt;means&lt;/em&gt; to call a function
(iterator) with a block was wrong.  In my semantics, calling a
function results in evaluating its arguments in the caller&amp;#8217;s
evaluation context, entering the evaluation context of the function,
binding the argument values, passing in the associated block, and then
evaluating the function body.  When &lt;em&gt;retry&lt;/em&gt; is called, I thus reasoned
mistakenly, the evaluation stops and begins anew at the beginning of
the function body, in this case, back at the &lt;em&gt;break&lt;/em&gt; expression.&lt;/p&gt;


	&lt;p&gt;But, clearly, that cannot be what is happening.  If it
were, the loop would never terminate.  The condition
&lt;em&gt;i&lt;/em&gt;&amp;#160;&amp;lt;&amp;#160;10 would be evaluated only once &amp;#8211; when the
&lt;em&gt;my_while&lt;/em&gt; function was called &amp;#8211; and thus &lt;em&gt;true&lt;/em&gt; would be forever bound to &lt;em&gt;cond&lt;/em&gt;
within the evaluation context of the function&amp;#8217;s body.&lt;/p&gt;


	&lt;p&gt;At this point, my brain went into hyper-curiosity mode.  Why &amp;#8211;
and more to the point, how &amp;#8211; does this thing work?  I
started looking for the calling semantics of Ruby.  (No luck finding
them, btw.)  Are arguments passed as thunks that get reevaluated upon
each access?  No, that seemed too wasteful and bizarre.&lt;/p&gt;


	&lt;p&gt;This is where the Pickaxe II let me down.  It said, &amp;#8220;&lt;em&gt;retry&lt;/em&gt; will
reevaluate any arguments to the iterator before restarting it.&amp;#8221;  Yes,
clearly, that is what is happening.  But how is it happening and
what exactly does that simple English statement really &lt;em&gt;mean&lt;/em&gt;?&lt;/p&gt;


	&lt;p&gt;So, after thinking about it, I concluded that what is going on is
that a function call in Ruby works like this.  Given a function &lt;em&gt;f&lt;/em&gt;,
a block &lt;em&gt;b&lt;/em&gt;, and arguments &lt;em&gt;xs&lt;/em&gt;, the call &lt;em&gt;f&lt;/em&gt;(&lt;em&gt;xs&lt;/em&gt;){&lt;em&gt;b&lt;/em&gt;}
means this:&lt;/p&gt;


	&lt;ol&gt;
	&lt;li&gt;let &lt;em&gt;k&lt;/em&gt; be the current continuation (i.e., just before the call)&lt;/li&gt;
		&lt;li&gt;evaulate &lt;em&gt;xs&lt;/em&gt; and bind the resulting values to &lt;em&gt;f&lt;/em&gt;&amp;#8217;s formal arguments&lt;/li&gt;
		&lt;li&gt;bind &lt;em&gt;b&lt;/em&gt; internally to the current block&lt;/li&gt;
		&lt;li&gt;evaluate the body of &lt;em&gt;f&lt;/em&gt;&lt;/li&gt;
	&lt;/ol&gt;


	&lt;p&gt;Now, if inside of &lt;em&gt;f&lt;/em&gt;&amp;#8217;s body we encounter a &lt;em&gt;retry&lt;/em&gt;, the evaluator
basically calls &lt;em&gt;k&lt;/em&gt; (with a &lt;em&gt;nil&lt;/em&gt; argument, I expect).  This jumps
back to step 2, from which evaluation continues.  Any side effects up
to this point are retained (so we could have previously incremented
&lt;em&gt;i&lt;/em&gt;, for example), which is what eventually allows the code within the function
body to choose an execution path which does not contain a
&lt;em&gt;retry&lt;/em&gt; expression, and thus avoid looping forever.&lt;/p&gt;


	&lt;p&gt;Just to make sure I &lt;em&gt;really&lt;/em&gt; had the semantics down, I wrote an
evaluator for a mini-Ruby in Haskell.  (I find that I understand something
better after I build it from the ground up.)&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='comment'&gt;{-# OPTIONS -fglasgow-exts #-}&lt;/span&gt;

&lt;span class='comment'&gt;-- Tom Moertel &amp;lt;tom@moertel.com&amp;gt;&lt;/span&gt;
&lt;span class='comment'&gt;-- 2005-03-26&lt;/span&gt;

&lt;span class='keyword'&gt;module&lt;/span&gt; &lt;span class='conid'&gt;MiniRuby&lt;/span&gt; &lt;span class='keyword'&gt;where&lt;/span&gt;

&lt;span class='keyword'&gt;import&lt;/span&gt; &lt;span class='conid'&gt;Control&lt;/span&gt;&lt;span class='varop'&gt;.&lt;/span&gt;&lt;span class='conid'&gt;Monad&lt;/span&gt;&lt;span class='varop'&gt;.&lt;/span&gt;&lt;span class='conid'&gt;Cont&lt;/span&gt;
&lt;span class='keyword'&gt;import&lt;/span&gt; &lt;span class='conid'&gt;Control&lt;/span&gt;&lt;span class='varop'&gt;.&lt;/span&gt;&lt;span class='conid'&gt;Monad&lt;/span&gt;&lt;span class='varop'&gt;.&lt;/span&gt;&lt;span class='conid'&gt;Reader&lt;/span&gt;
&lt;span class='keyword'&gt;import&lt;/span&gt; &lt;span class='conid'&gt;Control&lt;/span&gt;&lt;span class='varop'&gt;.&lt;/span&gt;&lt;span class='conid'&gt;Monad&lt;/span&gt;&lt;span class='varop'&gt;.&lt;/span&gt;&lt;span class='conid'&gt;State&lt;/span&gt;
&lt;span class='keyword'&gt;import&lt;/span&gt; &lt;span class='conid'&gt;Data&lt;/span&gt;&lt;span class='varop'&gt;.&lt;/span&gt;&lt;span class='conid'&gt;List&lt;/span&gt;
&lt;span class='keyword'&gt;import&lt;/span&gt; &lt;span class='conid'&gt;Data&lt;/span&gt;&lt;span class='varop'&gt;.&lt;/span&gt;&lt;span class='conid'&gt;Maybe&lt;/span&gt;

&lt;span class='comment'&gt;-- ========================================================&lt;/span&gt;
&lt;span class='comment'&gt;-- DATA TYPES&lt;/span&gt;

&lt;span class='comment'&gt;-- To keep things simple, there is only value type: string.&lt;/span&gt;

&lt;span class='keyword'&gt;type&lt;/span&gt; &lt;span class='conid'&gt;Identifier&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='conid'&gt;String&lt;/span&gt;
&lt;span class='keyword'&gt;type&lt;/span&gt; &lt;span class='conid'&gt;Value&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='conid'&gt;String&lt;/span&gt;

&lt;span class='comment'&gt;-- Evaluation occurs with in the continuation monad (which is used to&lt;/span&gt;
&lt;span class='comment'&gt;-- handle control flow), wrapped around a reader monad (which keeps&lt;/span&gt;
&lt;span class='comment'&gt;-- track of the calling context), wrapped around a state monad (which&lt;/span&gt;
&lt;span class='comment'&gt;-- keeps track of the evaluator's variables.)&lt;/span&gt;

&lt;span class='keyword'&gt;type&lt;/span&gt; &lt;span class='conid'&gt;Env&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='conid'&gt;Identifier&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt; &lt;span class='conid'&gt;Value&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt; &lt;span class='comment'&gt;-- identifiers =&amp;gt; values (strings)&lt;/span&gt;
&lt;span class='keyword'&gt;type&lt;/span&gt; &lt;span class='conid'&gt;RubyEvalCxt&lt;/span&gt; &lt;span class='varid'&gt;a&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='conid'&gt;ContT&lt;/span&gt; &lt;span class='varid'&gt;a&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='conid'&gt;ReaderT&lt;/span&gt; &lt;span class='conid'&gt;FcallCxt&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='conid'&gt;State&lt;/span&gt; &lt;span class='conid'&gt;Env&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varid'&gt;a&lt;/span&gt;
&lt;span class='keyword'&gt;data&lt;/span&gt; &lt;span class='conid'&gt;FcallCxt&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='conid'&gt;FC&lt;/span&gt; &lt;span class='layout'&gt;{&lt;/span&gt; &lt;span class='varid'&gt;retryCall&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Exp&lt;/span&gt;
                   &lt;span class='layout'&gt;,&lt;/span&gt; &lt;span class='varid'&gt;blockCont&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Value&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;Exp&lt;/span&gt; &lt;span class='layout'&gt;}&lt;/span&gt;

&lt;span class='comment'&gt;-- An expression is just something that can be evaluated to a value&lt;/span&gt;
&lt;span class='comment'&gt;-- within a ruby evaluation context.&lt;/span&gt;

&lt;span class='keyword'&gt;type&lt;/span&gt; &lt;span class='conid'&gt;Exp&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='conid'&gt;RubyEvalCxt&lt;/span&gt; &lt;span class='conid'&gt;Value&lt;/span&gt;

&lt;span class='comment'&gt;-- ========================================================&lt;/span&gt;
&lt;span class='comment'&gt;-- EVALUATOR&lt;/span&gt;

&lt;span class='comment'&gt;-- The following function evaluates an expression within a given&lt;/span&gt;
&lt;span class='comment'&gt;-- evaluation context.   The result is a value.&lt;/span&gt;

&lt;span class='varid'&gt;eval&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Env&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;FcallCxt&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;Exp&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;Value&lt;/span&gt;
&lt;span class='varid'&gt;eval&lt;/span&gt; &lt;span class='varid'&gt;env&lt;/span&gt; &lt;span class='varid'&gt;fc&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt;
    &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varop'&gt;`evalState`&lt;/span&gt; &lt;span class='varid'&gt;env&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varop'&gt;.&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varop'&gt;`runReaderT`&lt;/span&gt; &lt;span class='varid'&gt;fc&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varop'&gt;.&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varop'&gt;`runContT`&lt;/span&gt; &lt;span class='varid'&gt;return&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;

&lt;span class='comment'&gt;-- This version evaluates an expression at the "top level."&lt;/span&gt;

&lt;span class='varid'&gt;evalTop&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;eval&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt;
    &lt;span class='conid'&gt;FC&lt;/span&gt; &lt;span class='layout'&gt;{&lt;/span&gt; &lt;span class='varid'&gt;retryCall&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;return&lt;/span&gt; &lt;span class='str'&gt;"TOPLEVEL RETRY"&lt;/span&gt;
       &lt;span class='layout'&gt;,&lt;/span&gt; &lt;span class='varid'&gt;blockCont&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;const&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt; &lt;span class='varid'&gt;return&lt;/span&gt; &lt;span class='str'&gt;"TOPLEVEL BLOCK"&lt;/span&gt; &lt;span class='layout'&gt;}&lt;/span&gt;

&lt;span class='comment'&gt;-- A function call takes a function, a list of variable bindings, and&lt;/span&gt;
&lt;span class='comment'&gt;-- corresponding block.  It then creates a new execution context,&lt;/span&gt;
&lt;span class='comment'&gt;-- binds the variables, and evaluates the function body.&lt;/span&gt;

&lt;span class='varid'&gt;fcall&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Exp&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='conid'&gt;Identifier&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt; &lt;span class='conid'&gt;Exp&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;Exp&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;Exp&lt;/span&gt;
&lt;span class='varid'&gt;fcall&lt;/span&gt; &lt;span class='varid'&gt;fn&lt;/span&gt; &lt;span class='varid'&gt;args&lt;/span&gt; &lt;span class='varid'&gt;blk&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;callCC&lt;/span&gt; &lt;span class='varid'&gt;evalFn&lt;/span&gt;
  &lt;span class='keyword'&gt;where&lt;/span&gt;
    &lt;span class='varid'&gt;evalFn&lt;/span&gt; &lt;span class='varid'&gt;fnCont&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varop'&gt;`local`&lt;/span&gt; &lt;span class='keyword'&gt;do&lt;/span&gt; &lt;span class='layout'&gt;{&lt;/span&gt; &lt;span class='varid'&gt;bindArgs&lt;/span&gt;&lt;span class='layout'&gt;;&lt;/span&gt; &lt;span class='varid'&gt;fn&lt;/span&gt; &lt;span class='layout'&gt;}&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt; &lt;span class='keyglyph'&gt;\&lt;/span&gt;&lt;span class='varid'&gt;fc&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; 
        &lt;span class='varid'&gt;fc&lt;/span&gt; &lt;span class='layout'&gt;{&lt;/span&gt; &lt;span class='varid'&gt;retryCall&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;evalFn&lt;/span&gt; &lt;span class='varid'&gt;fnCont&lt;/span&gt; &lt;span class='varop'&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class='varid'&gt;fnCont&lt;/span&gt;
           &lt;span class='layout'&gt;,&lt;/span&gt; &lt;span class='varid'&gt;blockCont&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;const&lt;/span&gt; &lt;span class='varid'&gt;blk&lt;/span&gt; &lt;span class='layout'&gt;}&lt;/span&gt;
    &lt;span class='varid'&gt;bindArgs&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;mapM_&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;uncurry&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varop'&gt;=:=&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varid'&gt;args&lt;/span&gt;

&lt;span class='comment'&gt;-- Yield remembers the current continuation and passes control to the&lt;/span&gt;
&lt;span class='comment'&gt;-- associated block (which we get from the calling context).  For&lt;/span&gt;
&lt;span class='comment'&gt;-- extra spice, this version differs from Ruby's in that upon yielding&lt;/span&gt;
&lt;span class='comment'&gt;-- it makes the yielding function seem like a block to the block to&lt;/span&gt;
&lt;span class='comment'&gt;-- which it yields.  This lets the function and block yield back and&lt;/span&gt;
&lt;span class='comment'&gt;-- forth to each other, passing values along the way.&lt;/span&gt;

&lt;span class='varid'&gt;yield_&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;yield&lt;/span&gt; &lt;span class='str'&gt;"YIELD"&lt;/span&gt; &lt;span class='comment'&gt;-- yield w/o value&lt;/span&gt;

&lt;span class='varid'&gt;yield&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Value&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;Exp&lt;/span&gt;
&lt;span class='varid'&gt;yield&lt;/span&gt; &lt;span class='varid'&gt;value&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;callCC&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt; &lt;span class='keyglyph'&gt;\&lt;/span&gt;&lt;span class='varid'&gt;k&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='keyword'&gt;do&lt;/span&gt;
    &lt;span class='varid'&gt;bc&lt;/span&gt; &lt;span class='keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='varid'&gt;asks&lt;/span&gt; &lt;span class='varid'&gt;blockCont&lt;/span&gt;
    &lt;span class='varid'&gt;local&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='keyglyph'&gt;\&lt;/span&gt;&lt;span class='varid'&gt;fc&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='varid'&gt;fc&lt;/span&gt; &lt;span class='layout'&gt;{&lt;/span&gt; &lt;span class='varid'&gt;blockCont&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;k&lt;/span&gt; &lt;span class='layout'&gt;}&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;bc&lt;/span&gt; &lt;span class='varid'&gt;value&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;

&lt;span class='comment'&gt;-- Retry restarts the current computation w/in the calling context's&lt;/span&gt;
&lt;span class='comment'&gt;-- continuation.&lt;/span&gt;

&lt;span class='varid'&gt;retry&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Exp&lt;/span&gt;
&lt;span class='varid'&gt;retry&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='keyword'&gt;do&lt;/span&gt;
    &lt;span class='varid'&gt;k&lt;/span&gt; &lt;span class='keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='varid'&gt;asks&lt;/span&gt; &lt;span class='varid'&gt;retryCall&lt;/span&gt;
    &lt;span class='varid'&gt;k&lt;/span&gt;

&lt;span class='comment'&gt;-- ========================================================&lt;/span&gt;
&lt;span class='comment'&gt;-- VARIABLES&lt;/span&gt;

&lt;span class='comment'&gt;-- Bind associates a value with an identifier&lt;/span&gt;

&lt;span class='varid'&gt;bind&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Identifier&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;Value&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;Exp&lt;/span&gt;
&lt;span class='varid'&gt;bind&lt;/span&gt; &lt;span class='varid'&gt;i&lt;/span&gt; &lt;span class='varid'&gt;v&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='keyword'&gt;do&lt;/span&gt;
    &lt;span class='varid'&gt;modify&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;i&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt;&lt;span class='varid'&gt;v&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='conop'&gt;:&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;
    &lt;span class='varid'&gt;return&lt;/span&gt; &lt;span class='varid'&gt;v&lt;/span&gt;

&lt;span class='comment'&gt;-- More convenient syntax (=:=) for binding&lt;/span&gt;

&lt;span class='keyword'&gt;infixr&lt;/span&gt; &lt;span class='num'&gt;1&lt;/span&gt; &lt;span class='varop'&gt;=:=&lt;/span&gt;
&lt;span class='keyword'&gt;class&lt;/span&gt; &lt;span class='conid'&gt;Bindable&lt;/span&gt; &lt;span class='varid'&gt;v&lt;/span&gt; &lt;span class='keyword'&gt;where&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varop'&gt;=:=&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Identifier&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='varid'&gt;v&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;Exp&lt;/span&gt;
&lt;span class='keyword'&gt;instance&lt;/span&gt; &lt;span class='conid'&gt;Bindable&lt;/span&gt; &lt;span class='conid'&gt;Value&lt;/span&gt; &lt;span class='keyword'&gt;where&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varop'&gt;=:=&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;bind&lt;/span&gt;
&lt;span class='keyword'&gt;instance&lt;/span&gt; &lt;span class='conid'&gt;Bindable&lt;/span&gt; &lt;span class='conid'&gt;Exp&lt;/span&gt; &lt;span class='keyword'&gt;where&lt;/span&gt; &lt;span class='varid'&gt;i&lt;/span&gt; &lt;span class='varop'&gt;=:=&lt;/span&gt; &lt;span class='varid'&gt;e&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;bind&lt;/span&gt; &lt;span class='varid'&gt;i&lt;/span&gt; &lt;span class='varop'&gt;=&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class='varid'&gt;e&lt;/span&gt;

&lt;span class='comment'&gt;-- Lookup the value associated with an identifier&lt;/span&gt;

&lt;span class='varid'&gt;val&lt;/span&gt; &lt;span class='keyglyph'&gt;::&lt;/span&gt; &lt;span class='conid'&gt;Identifier&lt;/span&gt; &lt;span class='keyglyph'&gt;-&amp;gt;&lt;/span&gt; &lt;span class='conid'&gt;Exp&lt;/span&gt;
&lt;span class='varid'&gt;val&lt;/span&gt; &lt;span class='varid'&gt;i&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;gets&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt; &lt;span class='varid'&gt;fromMaybe&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;i&lt;/span&gt; &lt;span class='varop'&gt;++&lt;/span&gt; &lt;span class='str'&gt;"=UNDEFINED"&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varop'&gt;.&lt;/span&gt; &lt;span class='varid'&gt;lookup&lt;/span&gt; &lt;span class='varid'&gt;i&lt;/span&gt;

&lt;span class='comment'&gt;-- ========================================================&lt;/span&gt;
&lt;span class='comment'&gt;-- SAMPLE CODE&lt;/span&gt;

&lt;span class='comment'&gt;-- This code shows how "retry" works.  It is equivalent&lt;/span&gt;
&lt;span class='comment'&gt;-- to the following Ruby code:&lt;/span&gt;
&lt;span class='comment'&gt;-- &lt;/span&gt;
&lt;span class='comment'&gt;--   def my_while(cond)&lt;/span&gt;
&lt;span class='comment'&gt;--     if cond&lt;/span&gt;
&lt;span class='comment'&gt;--       yield&lt;/span&gt;
&lt;span class='comment'&gt;--       retry&lt;/span&gt;
&lt;span class='comment'&gt;--     end&lt;/span&gt;
&lt;span class='comment'&gt;--   end&lt;/span&gt;
&lt;span class='comment'&gt;-- &lt;/span&gt;
&lt;span class='comment'&gt;--   i = 0&lt;/span&gt;
&lt;span class='comment'&gt;--   my_while i &amp;lt; 10 do&lt;/span&gt;
&lt;span class='comment'&gt;--     i += 1&lt;/span&gt;
&lt;span class='comment'&gt;--   end  &lt;/span&gt;
&lt;span class='comment'&gt;-- &lt;/span&gt;
&lt;span class='comment'&gt;--   i&lt;/span&gt;

&lt;span class='varid'&gt;test1&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='keyword'&gt;do&lt;/span&gt;
    &lt;span class='str'&gt;"i"&lt;/span&gt; &lt;span class='varop'&gt;=:=&lt;/span&gt; &lt;span class='str'&gt;"0"&lt;/span&gt;
    &lt;span class='varid'&gt;my_while&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='str'&gt;"cond"&lt;/span&gt;&lt;span class='layout'&gt;,&lt;/span&gt; &lt;span class='varid'&gt;condExp&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt;
        &lt;span class='str'&gt;"i"&lt;/span&gt; &lt;span class='varop'&gt;+=&lt;/span&gt; &lt;span class='num'&gt;1&lt;/span&gt; &lt;span class='comment'&gt;-- block, passed to my_while&lt;/span&gt;
    &lt;span class='varid'&gt;val&lt;/span&gt; &lt;span class='str'&gt;"i"&lt;/span&gt;
  &lt;span class='keyword'&gt;where&lt;/span&gt;
    &lt;span class='varid'&gt;my_while&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;fcall&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt; &lt;span class='keyword'&gt;do&lt;/span&gt;
        &lt;span class='varid'&gt;cond&lt;/span&gt; &lt;span class='keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='varid'&gt;val&lt;/span&gt; &lt;span class='str'&gt;"cond"&lt;/span&gt;
        &lt;span class='keyword'&gt;if&lt;/span&gt; &lt;span class='varid'&gt;cond&lt;/span&gt; &lt;span class='varop'&gt;==&lt;/span&gt; &lt;span class='str'&gt;"true"&lt;/span&gt;
           &lt;span class='keyword'&gt;then&lt;/span&gt; &lt;span class='keyword'&gt;do&lt;/span&gt; &lt;span class='layout'&gt;{&lt;/span&gt; &lt;span class='varid'&gt;yield_&lt;/span&gt;&lt;span class='layout'&gt;;&lt;/span&gt; &lt;span class='varid'&gt;retry&lt;/span&gt; &lt;span class='layout'&gt;}&lt;/span&gt;
           &lt;span class='keyword'&gt;else&lt;/span&gt; &lt;span class='varid'&gt;return&lt;/span&gt; &lt;span class='varid'&gt;cond&lt;/span&gt;

    &lt;span class='comment'&gt;-- Ruby's += operator&lt;/span&gt;
    &lt;span class='varid'&gt;a&lt;/span&gt; &lt;span class='varop'&gt;+=&lt;/span&gt; &lt;span class='varid'&gt;b&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;a&lt;/span&gt; &lt;span class='varop'&gt;=:=&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;liftM&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt; &lt;span class='varid'&gt;show&lt;/span&gt; &lt;span class='varop'&gt;.&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;b&lt;/span&gt;&lt;span class='varop'&gt;+&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varop'&gt;.&lt;/span&gt; &lt;span class='varid'&gt;read&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;val&lt;/span&gt; &lt;span class='varid'&gt;a&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt;

    &lt;span class='comment'&gt;-- the expression "i &amp;lt; 10"&lt;/span&gt;
    &lt;span class='varid'&gt;condExp&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='keyword'&gt;do&lt;/span&gt;
        &lt;span class='varid'&gt;i&lt;/span&gt; &lt;span class='keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='varid'&gt;val&lt;/span&gt; &lt;span class='str'&gt;"i"&lt;/span&gt;
        &lt;span class='varid'&gt;return&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt; &lt;span class='keyword'&gt;if&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;read&lt;/span&gt; &lt;span class='varid'&gt;i&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varop'&gt;&amp;lt;&lt;/span&gt; &lt;span class='num'&gt;10&lt;/span&gt; &lt;span class='keyword'&gt;then&lt;/span&gt; &lt;span class='str'&gt;"true"&lt;/span&gt; &lt;span class='keyword'&gt;else&lt;/span&gt; &lt;span class='str'&gt;"false"&lt;/span&gt;

&lt;span class='comment'&gt;-- This example tests out yield.  It is equivalent to the following&lt;/span&gt;
&lt;span class='comment'&gt;-- Ruby code:&lt;/span&gt;
&lt;span class='comment'&gt;--&lt;/span&gt;
&lt;span class='comment'&gt;--   def f&lt;/span&gt;
&lt;span class='comment'&gt;--     @i = "I"&lt;/span&gt;
&lt;span class='comment'&gt;--     @k = "K"&lt;/span&gt;
&lt;span class='comment'&gt;--     yield&lt;/span&gt;
&lt;span class='comment'&gt;--   end&lt;/span&gt;
&lt;span class='comment'&gt;-- &lt;/span&gt;
&lt;span class='comment'&gt;--   f do&lt;/span&gt;
&lt;span class='comment'&gt;--     @j = "J"&lt;/span&gt;
&lt;span class='comment'&gt;--     @l = "L"&lt;/span&gt;
&lt;span class='comment'&gt;--   end&lt;/span&gt;
&lt;span class='comment'&gt;--&lt;/span&gt;
&lt;span class='comment'&gt;--   [@i,@j,@k,@l].join(" ")&lt;/span&gt;

&lt;span class='varid'&gt;test2&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='keyword'&gt;do&lt;/span&gt;
    &lt;span class='varid'&gt;f&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt; &lt;span class='keyword'&gt;do&lt;/span&gt;
        &lt;span class='str'&gt;"j"&lt;/span&gt; &lt;span class='varop'&gt;=:=&lt;/span&gt; &lt;span class='str'&gt;"J"&lt;/span&gt;
        &lt;span class='str'&gt;"l"&lt;/span&gt; &lt;span class='varop'&gt;=:=&lt;/span&gt; &lt;span class='str'&gt;"L"&lt;/span&gt;
    &lt;span class='varid'&gt;mapM&lt;/span&gt; &lt;span class='varid'&gt;val&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;words&lt;/span&gt; &lt;span class='str'&gt;"i j k l"&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varop'&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class='varid'&gt;return&lt;/span&gt; &lt;span class='varop'&gt;.&lt;/span&gt; &lt;span class='varid'&gt;unwords&lt;/span&gt;
  &lt;span class='keyword'&gt;where&lt;/span&gt;
    &lt;span class='varid'&gt;f&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;fcall&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt; &lt;span class='keyword'&gt;do&lt;/span&gt;
        &lt;span class='str'&gt;"i"&lt;/span&gt; &lt;span class='varop'&gt;=:=&lt;/span&gt; &lt;span class='str'&gt;"I"&lt;/span&gt;
        &lt;span class='str'&gt;"k"&lt;/span&gt; &lt;span class='varop'&gt;=:=&lt;/span&gt; &lt;span class='str'&gt;"K"&lt;/span&gt;
        &lt;span class='varid'&gt;yield_&lt;/span&gt;

&lt;span class='comment'&gt;-- This sample is somewhat trickier.  It uses the evaluator's&lt;/span&gt;
&lt;span class='comment'&gt;-- extended yield semantics to do what this Ruby code would&lt;/span&gt;
&lt;span class='comment'&gt;-- do if it were legal:&lt;/span&gt;
&lt;span class='comment'&gt;--&lt;/span&gt;
&lt;span class='comment'&gt;-- def f&lt;/span&gt;
&lt;span class='comment'&gt;--   @i = "I"&lt;/span&gt;
&lt;span class='comment'&gt;--   @j = yield&lt;/span&gt;
&lt;span class='comment'&gt;--   @k = "K"&lt;/span&gt;
&lt;span class='comment'&gt;--   @l = yield "Right-back-atcha!"&lt;/span&gt;
&lt;span class='comment'&gt;--   @m = yield&lt;/span&gt;
&lt;span class='comment'&gt;-- end&lt;/span&gt;
&lt;span class='comment'&gt;-- &lt;/span&gt;
&lt;span class='comment'&gt;-- f do&lt;/span&gt;
&lt;span class='comment'&gt;--   rba = yield "J-via-yield"  # not Ruby: yields *back* to f's body&lt;/span&gt;
&lt;span class='comment'&gt;--   yield rba&lt;/span&gt;
&lt;span class='comment'&gt;--   yield "M-via-yield"&lt;/span&gt;
&lt;span class='comment'&gt;-- end&lt;/span&gt;
&lt;span class='comment'&gt;--&lt;/span&gt;
&lt;span class='comment'&gt;-- [@i,@j,@k,@l,@m].join(" ")&lt;/span&gt;

&lt;span class='varid'&gt;test3&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='keyword'&gt;do&lt;/span&gt;
    &lt;span class='varid'&gt;f&lt;/span&gt; &lt;span class='keyglyph'&gt;[&lt;/span&gt;&lt;span class='keyglyph'&gt;]&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt; &lt;span class='keyword'&gt;do&lt;/span&gt;
        &lt;span class='varid'&gt;rba&lt;/span&gt; &lt;span class='keyglyph'&gt;&amp;lt;-&lt;/span&gt; &lt;span class='varid'&gt;yield&lt;/span&gt; &lt;span class='str'&gt;"J-via-yield"&lt;/span&gt;
        &lt;span class='varid'&gt;yield&lt;/span&gt; &lt;span class='varid'&gt;rba&lt;/span&gt;
        &lt;span class='varid'&gt;yield&lt;/span&gt; &lt;span class='str'&gt;"M-via-yield"&lt;/span&gt;
    &lt;span class='varid'&gt;mapM&lt;/span&gt; &lt;span class='varid'&gt;val&lt;/span&gt; &lt;span class='layout'&gt;(&lt;/span&gt;&lt;span class='varid'&gt;words&lt;/span&gt; &lt;span class='str'&gt;"i j k l m"&lt;/span&gt;&lt;span class='layout'&gt;)&lt;/span&gt; &lt;span class='varop'&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span class='varid'&gt;return&lt;/span&gt; &lt;span class='varop'&gt;.&lt;/span&gt; &lt;span class='varid'&gt;unwords&lt;/span&gt;
  &lt;span class='keyword'&gt;where&lt;/span&gt;
    &lt;span class='varid'&gt;f&lt;/span&gt; &lt;span class='keyglyph'&gt;=&lt;/span&gt; &lt;span class='varid'&gt;fcall&lt;/span&gt; &lt;span class='varop'&gt;$&lt;/span&gt; &lt;span class='keyword'&gt;do&lt;/span&gt;
        &lt;span class='str'&gt;"i"&lt;/span&gt; &lt;span class='varop'&gt;=:=&lt;/span&gt; &lt;span class='str'&gt;"I"&lt;/span&gt;
        &lt;span class='str'&gt;"j"&lt;/span&gt; &lt;span class='varop'&gt;=:=&lt;/span&gt; &lt;span class='varid'&gt;yield_&lt;/span&gt;
        &lt;span class='str'&gt;"k"&lt;/span&gt; &lt;span class='varop'&gt;=:=&lt;/span&gt; &lt;span class='str'&gt;"K"&lt;/span&gt;
        &lt;span class='str'&gt;"l"&lt;/span&gt; &lt;span class='varop'&gt;=:=&lt;/span&gt; &lt;span class='varid'&gt;yield&lt;/span&gt; &lt;span class='str'&gt;"Right-back-atcha!"&lt;/span&gt;
        &lt;span class='str'&gt;"m"&lt;/span&gt; &lt;span class='varop'&gt;=:=&lt;/span&gt; &lt;span class='varid'&gt;yield_&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;Here&amp;#8217;s what the code does when executed:&lt;/p&gt;


&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_haskell "&gt;&lt;span class='varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='varid'&gt;evalTop&lt;/span&gt; &lt;span class='varid'&gt;test1&lt;/span&gt;
&lt;span class='str'&gt;"10"&lt;/span&gt;

&lt;span class='varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='varid'&gt;evalTop&lt;/span&gt; &lt;span class='varid'&gt;test2&lt;/span&gt;
&lt;span class='str'&gt;"I J K L"&lt;/span&gt;

&lt;span class='varop'&gt;&amp;gt;&lt;/span&gt; &lt;span class='varid'&gt;evalTop&lt;/span&gt; &lt;span class='varid'&gt;test3&lt;/span&gt;
&lt;span class='str'&gt;"I J-via-yield K Right-back-atcha! M-via-yield"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

	&lt;p&gt;I must say that I really like Ruby&amp;#8217;s semantics.  So far, I find
Ruby to be a seriously cool programming language.&lt;/p&gt;</description>
      <pubDate>Fri, 25 Mar 2005 19:06:00 -0500</pubDate>
      <guid isPermaLink="false">urn:uuid:9cc5309b-0c55-4dfe-84ef-114a33fae84c</guid>
      <author>Tom Moertel</author>
      <link>http://blog.moertel.com/articles/2005/03/25/writing-a-simple-ruby-evaluator-in-haskell</link>
      <category>functional programming</category>
      <category>programming languages</category>
      <category>haskell</category>
      <category>ruby</category>
      <category>ruby</category>
      <category>haskell</category>
      <category>evaluators</category>
      <trackback:ping>http://blog.moertel.com/articles/trackback/150</trackback:ping>
    </item>
  </channel>
</rss>
