On Mon, Mar 28, 2011 at 6:28 PM, Michael Litchard <span dir="ltr">&lt;<a href="mailto:michael@schmong.org">michael@schmong.org</a>&gt;</span> wrote:<br><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">

1<br>
2<br>
3<br>
4<br>
5<br>
6<br>
7<br>
8<br>
9<br>
10<br>
11<br>
12<br>
13<br>
14<br>
15<br>
16<br>
17<br>
18<br>
19<br>
20<br>
21<br>
22<br>
23<br>
24<br>
25<br>
26<br>
27<br>
28<br>
29<br>
30<br>
31<br>
32<br>
33<br>
34<br>
35<br>
36<br>
37<br>
38<br>
39<br>
40<br>
41<br>
42<br>
43<br>
44<br>
45<br>
46<br>
47<br>
48<br>
49<br>
50<br>
51<br>
52<br>
53<br>
54<br>
55<br>
56<br>
57<br>
58<br>
59<br>
60<br>
61<br>
62<br>
63<br>
64<br>
65<br>
66<br>
67<br>
68<br>
69<br>
70<br>
71<br>
72<br>
73<br>
74<br>
75<br>
76<br>
77<br>
78<br>
79<br>
80<br>
81<br>
82<br>
83<br>
84<br>
85<br>
86<br>
87<br>
88<br>
89<br>
90<br>
91<br>
92<br>
93<br>
94<br>
95<br>
96<br>
97<br>
98<br>
99<br>
100<br>
101<br>
102<br>
103<br>
104<br>
105<br>
106<br>
107<br>
108<br>
109<br>
110<br>
111<br>
112<br>
113<br>
114<br>
115<br>
116<br>
117<br>
118<br>
119<br>
120<br>
121<br>
122<br>
123<br>
124<br>
125<br>
126<br>
127<br>
128<br>
129<br>
130<br>
131<br>
132<br>
133<br>
134<br>
135<br>
136<br>
137<br>
138<br>
139<br>
140<br>
141<br>
142<br>
143<br>
144<br>
145<br>
146<br>
147<br>
148<br>
149<br>
150<br>
151<br>
152<br>
153<br>
154<br>
155<br>
156<br>
157<br>
158<br>
159<br>
160<br>
161<br>
162<br>
163<br>
164<br>
165<br>
166<br>
167<br>
168<br>
169<br>
170<br>
171<br>
172<br>
173<br>
174<br>
175<br>
176<br>
177<br>
178<br>
179<br>
180<br>
181<br>
182<br>
183<br>
184<br>
185<br>
186<br>
187<br>
188<br>
189<br>
190<br>
191<br>
192<br>
193<br>
194<br>
195<br>
196<br>
197<br>
198<br>
199<br>
200<br>
201<br>
202<br>
203<br>
204<br>
205<br>
206<br>
207<br>
208<br>
209<br>
210<br>
211<br>
212<br>
213<br>
214<br>
215<br>
216<br>
217<br>
218<br>
219<br>
220<br>
221<br>
222<br>
223<br>
224<br>
225<br>
226<br>
227<br>
228<br>
229<br>
230<br>
231<br>
232<br>
233<br>
234<br>
235<br>
236<br>
237<br>
238<br>
239<br>
240<br>
241<br>
242<br>
243<br>
244<br>
245<br>
246<br>
247<br>
248<br>
249<br>
250<br>
251<br>
252<br>
253<br>
254<br>
255<br>
256<br>
257<br>
258<br>
259<br>
260<br>
261<br>
262<br>
263<br>
264<br>
265<br>
266<br>
267<br>
268<br>
269<br>
270<br>
271<br>
272<br>
273<br>
274<br>
275<br>
276<br>
277<br>
278<br>
279<br>
280<br>
281<br>
282<br>
283<br>
284<br>
285<br>
286<br>
287<br>
288<br>
289<br>
290<br>
291<br>
292<br>
293<br>
294<br>
295<br>
296<br>
297<br>
298<br>
299<br>
300<br>
301<br>
302<br>
303<br>
304<br>
305<br>
306<br>
307<br>
308<br>
309<br>
310<br>
311<br>
312<br>
313<br>
314<br>
315<br>
316<br>
317<br>
318<br>
319<br>
320<br>
321<br>
322<br>
323<br>
324<br>
325<br>
326<br>
327<br>
328<br>
329<br>
330<br>
331<br>
332<br>
333<br>
334<br>
335<br>
336<br>
337<br>
338<br>
339<br>
340<br>
341<br>
342<br>
343<br>
344<br>
345<br>
346<br>
347<br>
348<br>
349<br>
350<br>
351<br>
352<br>
353<br>
354<br>
355<br>
356<br>
357<br>
358<br>
359<br>
360<br>
361<br>
362<br>
363<br>
364<br>
365<br>
366<br>
367<br>
368<br></blockquote><div><br></div><div>Ready or not, here I come.</div><div><br></div><div>What is the purposes of these 368 numbers?</div><div><br></div><div>Luke</div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">


<br>
<br>
<br>
I&#39;m working off of a example file from Yesod, ajax.lhs<br>
I&#39;ve made an important change in types, and this has resulted in<br>
having to make the old code conform to the change. I will point out<br>
the specifics, then present my question. In the event I failed to<br>
include important information, I will paste in my code as well as the<br>
prototype.<br>
<br>
[Original]<br>
<br>
&gt; getHomeR :: Handler ()<br>
&gt; getHomeR = do<br>
&gt;   Ajax pages _ &lt;- getYesod<br>
&gt;   let first = head pages<br>
&gt;   redirect RedirectTemporary $ PageR $ pageSlug first<br>
<br>
[Changed]<br>
<br>
&gt; getHomeR :: Handler ()<br>
&gt; getHomeR = do<br>
&gt;   Tframe pages _ &lt;- getYesod<br>
&gt;   let first = head pages<br>
&gt;   redirect RedirectTemporary $ PageR $ pageSlug first<br>
<br>
Error Message<br>
<br>
test.lhs:62:4:<br>
    Constructor `Tframe&#39; should have 2 arguments, but has been given 1<br>
    In the pattern: Tframe pages<br>
    In a stmt of a &#39;do&#39; expression: Tframe pages &lt;- getYesod   ****<br>
This is not what I wrote *****<br>
    In the expression:<br>
        do { Tframe pages &lt;- getYesod;<br>
             content &lt;- widgetToPageContent widget;<br>
             hamletToRepHtml<br>
               (hamlet-0.7.1:Text.Hamlet.Quasi.toHamletValue<br>
                  (do { (hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad<br>
                       . preEscapedString)<br>
                          &quot;&lt;!DOCTYPE html&gt;&lt;html&gt;&lt;head&gt;&lt;title&gt;&quot;;<br>
                        (hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad<br>
                       . Text.Blaze.toHtml)<br>
                          (Main.pageTitle content);<br>
                        .... })) }<br>
<br>
As far as I can tell, I only made a cosmetic change. I don&#39;t know<br>
what&#39;s going on here.<br>
<br>
<br>
<br>
[Original]<br>
<br>
&gt; data Page = Page<br>
&gt;   { pageName :: String<br>
&gt;   , pageSlug :: String<br>
&gt;   , pageContent :: String     ******** I&#39;m going to change this **********<br>
&gt;   }<br>
<br>
[Changed]<br>
<br>
&gt; data Page = Page<br>
&gt;       { pageTitle :: String<br>
&gt;       , pageSlug :: String -- ^ used in the URL<br>
&gt;       , pageContent :: IO String           ******** This is the change *******<br>
&gt;       }<br>
<br>
<br>
Here&#39;s where I run into trouble<br>
<br>
[Original]<br>
&gt;   json page = jsonMap<br>
&gt;       [ (&quot;name&quot;, jsonScalar $ pageName page)<br>
&gt;       , (&quot;content&quot;, jsonScalar $ pageContent page)   ******** I&#39;m going to change this ********<br>
&gt;       ]<br>
<br>
<br>
<br>
[My changes]<br>
<br>
&gt;   json page = jsonMap<br>
&gt;       [ (&quot;name&quot;, jsonScalar $ Main.pageTitle page)<br>
&gt;       , (&quot;content&quot;, jsonScalar $ liftIO $ pageContent page) ******* This is the change ***********<br>
&gt;       ]<br>
<br>
<br>
Here&#39;s the compiler error<br>
<br>
test.lhs:107:35:<br>
    Couldn&#39;t match expected type `Char&#39; against inferred type `[Char]&#39;<br>
      Expected type: String<br>
      Inferred type: [String]<br>
    In the second argument of `($)&#39;, namely `liftIO $ pageContent page&#39;<br>
    In the expression: jsonScalar $ liftIO $ pageContent page<br>
Failed, modules loaded: none.<br>
<br>
<br>
I&#39;d appreciate a discussion about why this is wrong, and perhaps clues<br>
as to what is right.<br>
<br>
<br>
Last problem, stemming from the change in type to IO String. I don&#39;t<br>
have a clue as to what change I should make.<br>
<br>
test.lhs:100:25:<br>
    No instance for (Text.Blaze.ToHtml (IO String))<br>
      arising from a use of `Text.Blaze.toHtml&#39;<br>
                   at test.lhs:(100,25)-(103,3)<br>
    Possible fix:<br>
      add an instance declaration for (Text.Blaze.ToHtml (IO String))<br>
    In the second argument of `(.)&#39;, namely `Text.Blaze.toHtml&#39;<br>
    In a stmt of a &#39;do&#39; expression:<br>
        (hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad<br>
       . Text.Blaze.toHtml)<br>
          (pageContent page)<br>
    In the first argument of<br>
`hamlet-0.7.1:Text.Hamlet.Quasi.toHamletValue&#39;, nam<br>
<br>
                          ely<br>
        `do { (hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad<br>
             . preEscapedString)<br>
                &quot;&lt;h1&gt;&quot;;<br>
              (hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad<br>
             . Text.Blaze.toHtml)<br>
                (Main.pageTitle page);<br>
              (hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad<br>
             . preEscapedString)<br>
                &quot;&lt;/h1&gt;&lt;article&gt;&quot;;<br>
              (hamlet-0.7.1:Text.Hamlet.Quasi.htmlToHamletMonad<br>
             . Text.Blaze.toHtml)<br>
                (pageContent page);<br>
              .... }&#39;<br>
<br>
<br>
<br>
And finally, both files can be found below, if it is necessary to look at them.<br>
<br>
<br>
[Original]<br>
<br>
&lt;p&gt;We&#39;re going to write a very simple AJAX application. It will be a<br>
simple site with a few pages and a navbar; when you have Javascript,<br>
clicking on the links will load the pages via AJAX. Otherwise, it will<br>
use static HTML.&lt;/p&gt;<br>
<br>
&lt;p&gt;We&#39;re going to use jQuery for the Javascript, though anything would<br>
work just fine. Also, the AJAX responses will be served as JSON. Let&#39;s<br>
get started.&lt;/p&gt;<br>
<br>
&gt; {-# LANGUAGE ScopedTypeVariables, TypeFamilies, QuasiQuotes, TemplateHaskell, MultiParamTypeClasses #-}<br>
&gt; import Yesod<br>
&gt; import Yesod.Helpers.Static<br>
&gt; import Data.Monoid (mempty)<br>
<br>
Like the blog example, we&#39;ll define some data first.<br>
<br>
&gt; data Page = Page<br>
&gt;   { pageName :: String<br>
&gt;   , pageSlug :: String<br>
&gt;   , pageContent :: String<br>
&gt;   }<br>
<br>
&gt; loadPages :: IO [Page]<br>
&gt; loadPages = return<br>
&gt;   [ Page &quot;Page 1&quot; &quot;page-1&quot; &quot;My first page&quot;<br>
&gt;   , Page &quot;Page 2&quot; &quot;page-2&quot; &quot;My second page&quot;<br>
&gt;   , Page &quot;Page 3&quot; &quot;page-3&quot; &quot;My third page&quot;<br>
&gt;   ]<br>
<br>
 loadPages :: IO [Page]<br>
 loadPages = do<br>
<br>
&gt;<br>
&gt; data Ajax = Ajax<br>
&gt;   { ajaxPages :: [Page]<br>
&gt;   , ajaxStatic :: Static<br>
&gt;   }<br>
&gt; type Handler = GHandler Ajax Ajax<br>
<br>
Next we&#39;ll generate a function for each file in our static folder.<br>
This way, we get a compiler warning when trying to using a file which<br>
does not exist.<br>
<br>
&gt; staticFiles &quot;static/yesod/ajax&quot;<br>
<br>
Now the routes; we&#39;ll have a homepage, a pattern for the pages, and<br>
use a static subsite for the Javascript and CSS files.<br>
<br>
&gt; mkYesod &quot;Ajax&quot; [$parseRoutes|<br>
&gt; /                  HomeR   GET<br>
&gt; /page/#String      PageR   GET<br>
&gt; /static            StaticR Static ajaxStatic<br>
&gt; |]<br>
<br>
&lt;p&gt;That third line there is the syntax for a subsite: Static is the<br>
datatype for the subsite argument; siteStatic returns the site itself<br>
(parse, render and dispatch functions); and ajaxStatic gets the<br>
subsite argument from the master argument.&lt;/p&gt;<br>
<br>
&lt;p&gt;Now, we&#39;ll define the Yesod instance. We&#39;ll still use a dummy<br>
approot value, but we&#39;re also going to define a default layout.&lt;/p&gt;<br>
<br>
&gt; instance Yesod Ajax where<br>
&gt;   approot _ = &quot;&quot;<br>
&gt;   defaultLayout widget = do<br>
&gt;   Ajax pages _ &lt;- getYesod<br>
&gt;   content &lt;- widgetToPageContent widget<br>
&gt;   hamletToRepHtml [$hamlet|<br>
&gt; \&lt;!DOCTYPE html&gt;<br>
&gt;<br>
&gt; &lt;html&gt;<br>
&gt;   &lt;head&gt;<br>
&gt;     &lt;title&gt;#{pageTitle content}<br>
&gt;     &lt;link rel=&quot;stylesheet&quot; href=&quot;@{StaticR style_css}&quot;&gt;<br>
&gt;     &lt;script src=&quot;<a href="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" target="_blank">http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js</a>&quot;&gt;<br>
&gt;     &lt;script src=&quot;@{StaticR script_js}&quot;&gt;<br>
&gt;     \^{pageHead content}<br>
&gt;   &lt;body&gt;<br>
&gt;     &lt;ul id=&quot;navbar&quot;&gt;<br>
&gt;       $forall page &lt;- pages<br>
&gt;         &lt;li&gt;<br>
&gt;           &lt;a href=&quot;@{PageR (pageSlug page)}&quot;&gt;#{pageName page}<br>
&gt;     &lt;div id=&quot;content&quot;&gt;<br>
&gt;       \^{pageBody content}<br>
&gt; |]<br>
<br>
&lt;p&gt;The Hamlet template refers to style_css and style_js; these were<br>
generated by the call to staticFiles above.  There&#39;s nothing<br>
Yesod-specific about the &lt;a<br>
href=&quot;/static/yesod/ajax/style.css&quot;&gt;style.css&lt;/a&gt; and &lt;a<br>
href=&quot;/static/yesod/ajax/script.js&quot;&gt;script.js&lt;/a&gt; files, so I won&#39;t<br>
describe them here.&lt;/p&gt;<br>
<br>
&lt;p&gt;Now we need our handler functions. We&#39;ll have the homepage simply<br>
redirect to the first page, so:&lt;/p&gt;<br>
<br>
&gt; getHomeR :: Handler ()<br>
&gt; getHomeR = do<br>
&gt;   Ajax pages _ &lt;- getYesod<br>
&gt;   let first = head pages<br>
&gt;   redirect RedirectTemporary $ PageR $ pageSlug first<br>
<br>
And now the cool part: a handler that returns either HTML or JSON<br>
data, depending on the request headers.<br>
<br>
&gt; getPageR :: String -&gt; Handler RepHtmlJson<br>
&gt; getPageR slug = do<br>
&gt;   Ajax pages _ &lt;- getYesod<br>
&gt;   case filter (\e -&gt; pageSlug e == slug) pages of<br>
&gt;       [] -&gt; notFound<br>
&gt;       page:_ -&gt; defaultLayoutJson (do<br>
&gt;           setTitle $ string $ pageName page<br>
&gt;           addHamlet $ html page<br>
&gt;           ) (json page)<br>
&gt;  where<br>
&gt;   html page = [$hamlet|<br>
&gt; &lt;h1&gt;#{pageName page}<br>
&gt; &lt;article&gt;#{pageContent page}<br>
&gt; |]<br>
&gt;   json page = jsonMap<br>
&gt;       [ (&quot;name&quot;, jsonScalar $ pageName page)<br>
&gt;       , (&quot;content&quot;, jsonScalar $ pageContent page)<br>
&gt;       ]<br>
<br>
&lt;p&gt;We first try and find the appropriate Page, returning a 404 if it&#39;s<br>
not there. We then use the applyLayoutJson function, which is really<br>
the heart of this example. It allows you an easy way to create<br>
responses that will be either HTML or JSON, and which use the default<br>
layout in the HTML responses. It takes four arguments: 1) the title of<br>
the HTML page, 2) some value, 3) a function from that value to a<br>
Hamlet value, and 4) a function from that value to a Json value.&lt;/p&gt;<br>
<br>
&lt;p&gt;Under the scenes, the Json monad is really just using the Hamlet<br>
monad, so it gets all of the benefits thereof, namely interleaved IO<br>
and enumerator output. It is pretty straight-forward to generate JSON<br>
output by using the three functions jsonMap, jsonList and jsonMap. One<br>
thing to note: the input to jsonScalar must be HtmlContent; this helps<br>
avoid cross-site scripting attacks, by ensuring that any HTML entities<br>
will be escaped.&lt;/p&gt;<br>
<br>
&lt;p&gt;And now our typical main function. We need two parameters to build<br>
our Ajax value: the pages, and the static loader. We&#39;ll load up from a<br>
local directory.&lt;/p&gt;<br>
<br>
&gt; main :: IO ()<br>
&gt; main = do<br>
&gt;<br>
&gt;   pages &lt;- loadPages<br>
&gt;   let s = static &quot;static/yesod/ajax&quot;<br>
&gt;   warpDebug 3000 $ Ajax pages s<br>
<br>
<br>
[My changes]<br>
&gt; {-# LANGUAGE TypeFamilies, QuasiQuotes, TemplateHaskell, MultiParamTypeClasses<br>
&gt;  #-}<br>
&gt; import Yesod<br>
&gt; import Yesod.Helpers.Static<br>
&gt; import System.Environment<br>
&gt; import System.IO<br>
&gt; import System.Directory<br>
&gt; import System.FilePath.Posix<br>
&gt; import Control.Applicative<br>
&gt; import Data.List.Split<br>
<br>
<br>
<br>
&gt; data Page = Page<br>
&gt;       { pageTitle :: String<br>
&gt;       , pageSlug :: String -- ^ used in the URL<br>
&gt;       , pageContent :: IO String<br>
&gt;       }<br>
<br>
<br>
<br>
&gt; loadPage :: IO [Page]<br>
&gt; loadPage = do<br>
&gt;  let directoryPath = &quot;/home/mlitchard/playground/webTests/files&quot;<br>
&gt;  let processedPath = map (directoryPath &lt;/&gt;) . filter (`notElem` [&quot;.&quot;,&quot;..&quot;])<br>
&gt;  pageFileNames &lt;- processedPath &lt;$&gt; getDirectoryContents directoryPath<br>
&gt;  let pageFiles = map readFile pageFileNames<br>
&gt;  return $ zipWith popEntries pageFileNames pageFiles<br>
<br>
-- &gt;  return $ zipWith popEntries<br>
<br>
&gt; popEntries :: FilePath -&gt; IO String -&gt; Page<br>
&gt; popEntries pageFileName pageFile =<br>
&gt;   let pageT = last $ splitOn &quot;/&quot; pageFileName<br>
&gt;       pageS = &quot;Job&quot; ++ pageT<br>
&gt;   in  Page { Main.pageTitle=pageT,<br>
&gt;              pageSlug=pageS,<br>
&gt;              pageContent=pageFile }<br>
<br>
&gt; data Tframe = Tframe<br>
&gt;   { tframePages :: [Page]<br>
&gt;   , tframeStatic :: Static<br>
&gt;   }<br>
<br>
&gt; type Handler = GHandler Tframe Tframe<br>
<br>
&gt; staticFiles &quot;static/yesod/ajax&quot;<br>
<br>
Routes<br>
<br>
&gt; mkYesod &quot;Tframe&quot; [$parseRoutes|<br>
&gt; /                    HomeR   GET<br>
&gt; /page/#String        PageR   GET<br>
&gt; /static              StaticR Static tframeStatic<br>
&gt; |]<br>
<br>
defining the Yesod instance<br>
<br>
&gt; instance Yesod Tframe where<br>
&gt;   approot _ = &quot;&quot;<br>
&gt;   defaultLayout widget = do<br>
&gt;   Tframe pages &lt;- getYesod<br>
&gt;   content &lt;- widgetToPageContent widget<br>
&gt;   hamletToRepHtml [$hamlet|<br>
&gt; \&lt;!DOCTYPE html&gt;<br>
&gt;<br>
&gt; &lt;html&gt;<br>
&gt;   &lt;head&gt;<br>
&gt;     &lt;title&gt;#{Main.pageTitle content}<br>
&gt;     &lt;link rel=&quot;stylesheet&quot; href=&quot;@{StaticR style_css}&quot;&gt;<br>
&gt;     &lt;script src=&quot;<a href="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" target="_blank">http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js</a>&quot;&gt;<br>
&gt;     &lt;script src=&quot;@{StaticR script_js}&quot;&gt;<br>
&gt;     \^{pageHead content}<br>
&gt;   &lt;body&gt;<br>
&gt;     &lt;ul id=&quot;navbar&quot;&gt;<br>
&gt;       $forall page &lt;- pages<br>
&gt;         &lt;li&gt;<br>
&gt;           &lt;a href=&quot;@{PageR (pageSlug page)}&quot;&gt;#{Main.pageTitle page}<br>
&gt;     &lt;div id=&quot;content&quot;&gt;<br>
&gt;       \^{pageBody content}<br>
&gt; |]<br>
<br>
<br>
&gt; getHomeR :: Handler ()<br>
&gt; getHomeR = do<br>
&gt;   Tframe pages _ &lt;- getYesod<br>
&gt;   let first = head pages<br>
&gt;   redirect RedirectTemporary $ PageR $ pageSlug first<br>
<br>
&gt; getPageR :: String -&gt; Handler RepHtmlJson<br>
&gt; getPageR slug = do<br>
&gt;   Tframe pages _ &lt;- getYesod<br>
&gt;   case filter (\e -&gt; pageSlug e == slug) pages of<br>
&gt;       [] -&gt; notFound<br>
&gt;       page:_ -&gt; defaultLayoutJson (do<br>
&gt;           setTitle $ string $ Main.pageTitle page<br>
&gt;           addHamlet $ html page<br>
&gt;           ) (json page)<br>
&gt;  where<br>
&gt;   html page = [$hamlet|<br>
&gt; &lt;h1&gt;#{Main.pageTitle page}<br>
&gt; &lt;article&gt;#{pageContent page}<br>
&gt; |]<br>
<br>
&gt;   json page = jsonMap<br>
&gt;       [ (&quot;name&quot;, jsonScalar $ Main.pageTitle page)<br>
&gt;       , (&quot;content&quot;, jsonScalar $ liftIO $ pageContent page)<br>
&gt;       ]<br>
<br>
&gt; main :: IO ()<br>
&gt; main = do<br>
&gt;<br>
&gt;   pages &lt;- loadPage<br>
&gt;   let s = static &quot;static/yesod/ajax&quot;<br>
&gt;   warpDebug 3000 $ Tframe pages s<br>
<br>
<br>
If you&#39;ve read to the bottom, thanks for your patience. I appreciate<br>
any illumination you can send my way.<br>
<br>
<br>
Michael<br>
<br>
_______________________________________________<br>
Haskell-Cafe mailing list<br>
<a href="mailto:Haskell-Cafe@haskell.org">Haskell-Cafe@haskell.org</a><br>
<a href="http://www.haskell.org/mailman/listinfo/haskell-cafe" target="_blank">http://www.haskell.org/mailman/listinfo/haskell-cafe</a><br>
</blockquote></div><br>