On Wed, Jun 8, 2011 at 8:17 PM, Gregory Guthrie <span dir="ltr">&lt;<a href="mailto:guthrie@mum.edu">guthrie@mum.edu</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;">
An earlier note on students reactions to the imperative style forced on them by some Haskell libraries (&quot;do ...&quot;) is interesting, and seems similar to an observation in a project I was developing for students; making a version of a simple lab from previous SML assignment.<br>

<br>
It uses a dictionary to do some statistics for a text analysis, but since the dictionary is read at a leaf node of the analysis algorithm, that function must be IO(), and thus the analysis using it also, ... etc. all the way up to the top.<br>

<br>
So the implication of the rules:<br>
  1) all IO must start from the top level, and there is only one IO<br>
  2) you cannot extract anything from an IO<br>
<br>
Seems to be that the whole program structure becomes a series of do... blocks, which is basically a sequential imperative looking style.<br>
The general advice of &quot;Strive to keep as much of the program pure as possible&quot; thus seems difficult.<br>
<br>
</blockquote><div><br></div><div>You&#39;re right, in some ways.  But it is not difficult to stay out of IO.  Just don&#39;t use the  (IO a) type if you don&#39;t need to do input and output. I strongly suspect that &quot;you&quot; are starting your program writing process by writing IO actions, which &quot;naturally&quot; leads to writing a tree of IO actions.  Start by writing data types and transformation functions instead. &quot;Every&quot; program does three things:  take some kind of input, apply a transformation, and yield some kind of output.</div>
<div><br></div><div>Strive to define datatypes that capture the program&#39;s logic.  For example, enumerate your cases in an abstract data type.  In this way, you can move the program logic into the pure fragment of the language, as opposed to using explicit if-then-else&#39;s in the IO monad.</div>
<div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">An option I suppose would be to read the dictionary at the top level, and then pass it all the way down to the analysis routine that uses it, but that exposes the details of how the analysis is done, and couples the top and bottom levels of the previously modular functions.<br>

<br>
While this is all logical and understandable, it is quite different than how I did this in SML; where I could encapsulate the IO in the analysis function. It was a local secret of the analysis what data it needed, and where it came from. Note that (of course...) if the dictionary was static and an internal data structure, then this would all go away. It is interesting to me (and curious at first) that one could not somehow treat a &quot;constant&quot; data definition file resource in a more encapsulated manner, and not have it ripple all the way up through the program because it was &quot;impure&quot; once converted to an external definition (=IO). My first impulse was to read the dictionary in a do... and then try to extract it and go merrily on, but that won&#39;t work - by design of course!<br>
</blockquote><div><br></div><div><br></div><div>I don&#39;t know how ML handles IO, but Haskell is a lazy language.  In order for a value to be computed, some other value has to &quot;request&quot; it.  And programs usually have a single entry point -- main :: IO a.  So it is going to be the thing that requests computations to be done.  On the other hand, it can request a series of IO actions to determine program flow/logic, or it can request pure computations to do the same.</div>
<div> </div></div>