Difference between revisions of "Tutorials/Programming Haskell/String IO"

From HaskellWiki
Jump to navigation Jump to search
(today's code)
 
(fmt)
Line 18: Line 18:
 
Haskell programs are executed by evaluating the special 'main' function.
 
Haskell programs are executed by evaluating the special 'main' function.
   
  +
<haskell>
<pre> <span class='keyword'>import</span> <span class='conid'>Data</span><span class='varop'>.</span><span class='conid'>List</span>
 
  +
import Data.List
 
<span class='varid'>mylength</span> <span class='keyglyph'>=</span> <span class='varid'>foldr</span> <span class='layout'>(</span><span class='varid'>const</span> <span class='layout'>(</span><span class='varop'>+</span><span class='num'>1</span><span class='layout'>)</span><span class='layout'>)</span> <span class='num'>0</span>
 
<span class='varid'>main</span> <span class='keyglyph'>=</span> <span class='varid'>print</span> <span class='layout'>(</span><span class='varid'>mylength</span> <span class='str'>"haskell"</span><span class='layout'>)</span>
 
</pre>
 
   
  +
mylength = foldr (const (+1)) 0
  +
main = print (mylength "haskell")
  +
</haskell>
   
 
To compile this to native code, we would feed the source file to the compiler:
 
To compile this to native code, we would feed the source file to the compiler:
   
  +
$ ghc A.hs
<pre> <span class='varop'>$</span> <span class='varid'>ghc</span> <span class='conid'>A</span><span class='varop'>.</span><span class='varid'>hs</span>
 
  +
$ ./a.out
<span class='varop'>$</span> <span class='varop'>./</span><span class='varid'>a</span><span class='varop'>.</span><span class='varid'>out</span>
 
  +
7
<span class='num'>7</span>
 
</pre>
 
 
   
 
For a faster turnaround, we can run the code directly through
 
For a faster turnaround, we can run the code directly through
 
the bytecode interpreter, GHCi, using the 'runhaskell' program:
 
the bytecode interpreter, GHCi, using the 'runhaskell' program:
   
  +
$ runhaskell A.hs
<pre> <span class='varop'>$</span> <span class='varid'>runhaskell</span> <span class='conid'>A</span><span class='varop'>.</span><span class='varid'>hs</span>
 
  +
7
<span class='num'>7</span>
 
</pre>
 
 
   
 
GHCi, the interactive Haskell environment, is a little bit different.
 
GHCi, the interactive Haskell environment, is a little bit different.
Line 48: Line 44:
 
<i>do</i>-notation at the GHCi prompt to define new functions:
 
<i>do</i>-notation at the GHCi prompt to define new functions:
   
  +
$ ghci
<pre> <span class='varop'>$</span> <span class='varid'>ghci</span>
 
  +
Prelude> :m + Data.List
<span class='conid'>Prelude</span><span class='varop'>&gt;</span> <span class='conop'>:</span><span class='varid'>m</span> <span class='varop'>+</span> <span class='conid'>Data</span><span class='varop'>.</span><span class='conid'>List</span>
 
   
  +
Prelude> let mylength = foldr (const (+1)) 0
<span class='conid'>Prelude</span><span class='varop'>&gt;</span> <span class='keyword'>let</span> <span class='varid'>mylength</span> <span class='keyglyph'>=</span> <span class='varid'>foldr</span> <span class='layout'>(</span><span class='varid'>const</span> <span class='layout'>(</span><span class='varop'>+</span><span class='num'>1</span><span class='layout'>)</span><span class='layout'>)</span> <span class='num'>0</span>
 
   
  +
Prelude> :t mylength
<span class='conid'>Prelude</span><span class='varop'>&gt;</span> <span class='conop'>:</span><span class='varid'>t</span> <span class='varid'>mylength</span>
 
  +
mylength :: [a] -> Integer
<span class='varid'>mylength</span> <span class='keyglyph'>::</span> <span class='keyglyph'>[</span><span class='varid'>a</span><span class='keyglyph'>]</span> <span class='keyglyph'>-&gt;</span> <span class='conid'>Integer</span>
 
 
<span class='conid'>Prelude</span><span class='varop'>&gt;</span> <span class='varid'>mylength</span> <span class='str'>"haskell"</span>
 
<span class='num'>7</span>
 
</pre>
 
   
  +
Prelude> mylength "haskell"
  +
7
   
 
For this tutorial I will be developing code in a source file, and either
 
For this tutorial I will be developing code in a source file, and either
Line 65: Line 59:
 
To load a source file into GHCi, we do:
 
To load a source file into GHCi, we do:
   
  +
$ ghci
<pre> <span class='varop'>$</span> <span class='varid'>ghci</span>
 
  +
Prelude> :load A.hs
<span class='conid'>Prelude</span><span class='varop'>&gt;</span> <span class='conop'>:</span><span class='varid'>load</span> <span class='conid'>A</span><span class='varop'>.</span><span class='varid'>hs</span>
 
 
<span class='varop'>*</span><span class='conid'>Main</span><span class='varop'>&gt;</span> <span class='conop'>:</span><span class='varid'>t</span> <span class='varid'>main</span>
 
<span class='varid'>main</span> <span class='keyglyph'>::</span> <span class='conid'>IO</span> <span class='layout'>(</span><span class='layout'>)</span>
 
   
  +
*Main> :t main
<span class='varop'>*</span><span class='conid'>Main</span><span class='varop'>&gt;</span> <span class='conop'>:</span><span class='varid'>t</span> <span class='varid'>mylength</span>
 
  +
main :: IO ()
<span class='varid'>mylength</span> <span class='keyglyph'>::</span> <span class='keyglyph'>[</span><span class='varid'>a</span><span class='keyglyph'>]</span> <span class='keyglyph'>-&gt;</span> <span class='conid'>Integer</span>
 
   
  +
*Main> :t mylength
<span class='varop'>*</span><span class='conid'>Main</span><span class='varop'>&gt;</span> <span class='varid'>mylength</span> <span class='str'>"foo"</span>
 
  +
mylength :: [a] -> Integer
<span class='num'>3</span>
 
   
  +
*Main> mylength "foo"
<span class='varop'>*</span><span class='conid'>Main</span><span class='varop'>&gt;</span> <span class='varid'>main</span>
 
  +
3
<span class='num'>7</span>
 
</pre>
 
   
  +
*Main> main
  +
7
   
 
Now, let's get into the code!
 
Now, let's get into the code!
Line 105: Line 97:
 
use the 'interact' function:
 
use the 'interact' function:
   
  +
<haskell>
<pre> <span class='varid'>interact</span> <span class='keyglyph'>::</span> <span class='layout'>(</span><span class='conid'>String</span> <span class='keyglyph'>-&gt;</span> <span class='conid'>String</span><span class='layout'>)</span> <span class='keyglyph'>-&gt;</span> <span class='conid'>IO</span> <span class='layout'>(</span><span class='layout'>)</span>
 
  +
interact :: (String -> String) -> IO ()
</pre>
 
  +
</haskell>
 
   
 
This <i>higher order</i> function takes, as an argument, some function for
 
This <i>higher order</i> function takes, as an argument, some function for
Line 115: Line 107:
 
For example, we can write the 'cat' unix program as:
 
For example, we can write the 'cat' unix program as:
   
  +
<haskell>
<pre> <span class='varid'>main</span> <span class='keyglyph'>=</span> <span class='varid'>interact</span> <span class='varid'>id</span>
 
  +
main = interact id
</pre>
 
  +
</haskell>
 
   
 
Yes, that's it! Let's compile and run this program:
 
Yes, that's it! Let's compile and run this program:
   
  +
$ ghc -O A.hs
<pre> <span class='varop'>$</span> <span class='varid'>ghc</span> <span class='comment'>-</span><span class='conid'>O</span> <span class='conid'>A</span><span class='varop'>.</span><span class='varid'>hs</span>
 
 
<span class='varop'>$</span> <span class='varid'>cat</span> <span class='conid'>A</span><span class='varop'>.</span><span class='varid'>hs</span> <span class='keyglyph'>|</span> <span class='varop'>./</span><span class='varid'>a</span><span class='varop'>.</span><span class='varid'>out</span>
 
<span class='varid'>main</span> <span class='keyglyph'>=</span> <span class='varid'>interact</span> <span class='varid'>id</span>
 
</pre>
 
   
  +
$ cat A.hs | ./a.out
  +
main = interact id
   
 
How does this work? Firstly, 'interact' is defined as:
 
How does this work? Firstly, 'interact' is defined as:
   
  +
<haskell>
<pre> <span class='varid'>interact</span> <span class='varid'>f</span> <span class='keyglyph'>=</span> <span class='keyword'>do</span> <span class='varid'>s</span> <span class='keyglyph'>&lt;-</span> <span class='varid'>getContents</span>
 
  +
interact f = do s <- getContents
<span class='varid'>putStr</span> <span class='layout'>(</span><span class='varid'>f</span> <span class='varid'>s</span><span class='layout'>)</span>
 
  +
putStr (f s)
</pre>
 
  +
</haskell>
 
   
 
So it reads a string from standard input, and writes to standard output
 
So it reads a string from standard input, and writes to standard output
Line 139: Line 129:
 
function itself has the type:
 
function itself has the type:
   
  +
<haskell>
<pre> <span class='varid'>id</span> <span class='keyglyph'>::</span> <span class='varid'>a</span> <span class='keyglyph'>-&gt;</span> <span class='varid'>a</span>
 
  +
id :: a -> a
</pre>
 
  +
</haskell>
 
   
 
'id' is a function of one argument, of any type (the lowercase 'a' in
 
'id' is a function of one argument, of any type (the lowercase 'a' in
Line 149: Line 139:
 
the same type. There's is only one (non-trivial) function of this type:
 
the same type. There's is only one (non-trivial) function of this type:
   
  +
<haskell>
<pre> <span class='varid'>id</span> <span class='varid'>a</span> <span class='keyglyph'>=</span> <span class='varid'>a</span>
 
  +
id a = a
</pre>
 
  +
</haskell>
 
   
 
So 'interact id' will print to the input string to standard output
 
So 'interact id' will print to the input string to standard output
 
unmodified.
 
unmodified.
 
   
 
Let's now write the 'wc' program:
 
Let's now write the 'wc' program:
   
  +
<haskell>
<pre> <span class='varid'>main</span> <span class='keyglyph'>=</span> <span class='varid'>interact</span> <span class='varid'>count</span>
 
  +
main = interact count
<span class='varid'>count</span> <span class='varid'>s</span> <span class='keyglyph'>=</span> <span class='varid'>show</span> <span class='layout'>(</span><span class='varid'>length</span> <span class='varid'>s</span><span class='layout'>)</span> <span class='varop'>++</span> <span class='str'>"\n"</span>
 
  +
count s = show (length s) ++ "\n"
</pre>
 
  +
</haskell>
 
   
 
This will print the length of the input string, that is, the number of
 
This will print the length of the input string, that is, the number of
 
chars:
 
chars:
   
  +
$ runhaskell A.hs < A.hs
<pre> <span class='varop'>$</span> <span class='varid'>runhaskell</span> <span class='conid'>A</span><span class='varop'>.</span><span class='varid'>hs</span> <span class='varop'>&lt;</span> <span class='conid'>A</span><span class='varop'>.</span><span class='varid'>hs</span>
 
  +
57
<span class='num'>57</span>
 
</pre>
 
   
 
==Line oriented IO==
 
==Line oriented IO==
Line 178: Line 166:
 
'lines' function, defined in the [http://haskell.org/ghc/docs/latest/html/libraries/base/Data-List.html Data.List] library:
 
'lines' function, defined in the [http://haskell.org/ghc/docs/latest/html/libraries/base/Data-List.html Data.List] library:
   
  +
<haskell>
<pre> <span class='varid'>lines</span> <span class='keyglyph'>::</span> <span class='conid'>String</span> <span class='keyglyph'>-&gt;</span> <span class='keyglyph'>[</span><span class='conid'>String</span><span class='keyglyph'>]</span>
 
  +
lines :: String -> [String]
</pre>
 
  +
</haskell>
 
   
 
The type, once again, tells the story. 'lines' takes a String, and
 
The type, once again, tells the story. 'lines' takes a String, and
Line 187: Line 175:
 
we'd use the ... 'unlines' function:
 
we'd use the ... 'unlines' function:
   
  +
<haskell>
<pre> <span class='varid'>unlines</span> <span class='keyglyph'>::</span> <span class='keyglyph'>[</span><span class='conid'>String</span><span class='keyglyph'>]</span> <span class='keyglyph'>-&gt;</span> <span class='conid'>String</span>
 
  +
unlines :: [String] -> String
</pre>
 
  +
</haskell>
 
   
 
There are also similar functions for splitting on words, namely 'words'
 
There are also similar functions for splitting on words, namely 'words'
 
and 'unwords'. Now, an example. To count the number of lines in a file:
 
and 'unwords'. Now, an example. To count the number of lines in a file:
   
  +
<haskell>
<pre> <span class='varid'>main</span> <span class='keyglyph'>=</span> <span class='varid'>interact</span> <span class='layout'>(</span><span class='varid'>count</span> <span class='varop'>.</span> <span class='varid'>lines</span><span class='layout'>)</span>
 
  +
main = interact (count . lines)
</pre>
 
  +
</haskell>
 
   
 
We can run this as:
 
We can run this as:
   
  +
$ ghc -O A.hs
<pre> <span class='varop'>$</span> <span class='varid'>ghc</span> <span class='comment'>-</span><span class='conid'>O</span> <span class='conid'>A</span><span class='varop'>.</span><span class='varid'>hs</span>
 
 
<span class='varop'>$</span> <span class='varop'>./</span><span class='varid'>a</span><span class='varop'>.</span><span class='varid'>out</span> <span class='varop'>&lt;</span> <span class='conid'>A</span><span class='varop'>.</span><span class='varid'>hs</span>
 
<span class='num'>3</span>
 
</pre>
 
   
  +
$ ./a.out < A.hs
  +
3
   
 
Here we reuse the 'count' function from above, by <i>composing</i> it
 
Here we reuse the 'count' function from above, by <i>composing</i> it
Line 220: Line 206:
 
The (.) function is just a normal everyday Haskell function, defined as:
 
The (.) function is just a normal everyday Haskell function, defined as:
   
  +
<haskell>
<pre> <span class='layout'>(</span><span class='varop'>.</span><span class='layout'>)</span> <span class='varid'>f</span> <span class='varid'>g</span> <span class='varid'>x</span> <span class='keyglyph'>=</span> <span class='varid'>f</span> <span class='layout'>(</span><span class='varid'>g</span> <span class='varid'>x</span><span class='layout'>)</span>
 
  +
(.) f g x = f (g x)
</pre>
 
  +
</haskell>
 
   
 
This looks a little like magic (or line noise), but its pretty easy. The
 
This looks a little like magic (or line noise), but its pretty easy. The
Line 230: Line 216:
 
be of any type. The type of (.) is actually:
 
be of any type. The type of (.) is actually:
   
  +
<haskell>
<pre> <span class='layout'>(</span><span class='varop'>.</span><span class='layout'>)</span> <span class='keyglyph'>::</span> <span class='layout'>(</span><span class='varid'>b</span> <span class='keyglyph'>-&gt;</span> <span class='varid'>c</span><span class='layout'>)</span> <span class='keyglyph'>-&gt;</span> <span class='layout'>(</span><span class='varid'>a</span> <span class='keyglyph'>-&gt;</span> <span class='varid'>b</span><span class='layout'>)</span> <span class='keyglyph'>-&gt;</span> <span class='varid'>a</span> <span class='keyglyph'>-&gt;</span> <span class='varid'>c</span>
 
  +
(.) :: (b -> c) -> (a -> b) -> a -> c
</pre>
 
  +
</haskell>
 
   
 
which might look a bit hairy, but it essentially specifies what types of
 
which might look a bit hairy, but it essentially specifies what types of
 
arguments make sense to compose. That is, only those where:
 
arguments make sense to compose. That is, only those where:
   
  +
<haskell>
<pre> <span class='varid'>f</span> <span class='keyglyph'>::</span> <span class='varid'>b</span> <span class='keyglyph'>-&gt;</span> <span class='varid'>c</span>
 
  +
f :: b -> c
<span class='varid'>g</span> <span class='keyglyph'>::</span> <span class='varid'>a</span> <span class='keyglyph'>-&gt;</span> <span class='varid'>b</span>
 
  +
g :: a -> b
<span class='varid'>x</span> <span class='keyglyph'>::</span> <span class='varid'>a</span>
 
  +
x :: a
</pre>
 
  +
</haskell>
 
   
 
can be composed, yielding a new function of type:
 
can be composed, yielding a new function of type:
   
  +
<haskell>
<pre> <span class='layout'>(</span><span class='varid'>f</span> <span class='varop'>.</span> <span class='varid'>g</span><span class='layout'>)</span> <span class='keyglyph'>::</span> <span class='varid'>a</span> <span class='keyglyph'>-&gt;</span> <span class='varid'>c</span>
 
  +
(f . g) :: a -> c
</pre>
 
  +
</haskell>
 
   
 
The nice thing is that this composition makes sense (and works) <i>for all types a, b and
 
The nice thing is that this composition makes sense (and works) <i>for all types a, b and
Line 269: Line 255:
 
'rev' command):
 
'rev' command):
   
  +
<haskell>
<pre> <span class='varid'>main</span> <span class='keyglyph'>=</span> <span class='varid'>interact</span> <span class='layout'>(</span><span class='varid'>unlines</span> <span class='varop'>.</span> <span class='varid'>map</span> <span class='varid'>reverse</span> <span class='varop'>.</span> <span class='varid'>lines</span><span class='layout'>)</span>
 
  +
main = interact (unlines . map reverse . lines)
</pre>
 
  +
</haskell>
   
 
Which when run, reverses the input lines:
 
Which when run, reverses the input lines:
   
  +
$ ./a.out < B.hs
<pre> <span class='varop'>$</span> <span class='varop'>./</span><span class='varid'>a</span><span class='varop'>.</span><span class='varid'>out</span> <span class='varop'>&lt;</span> <span class='conid'>B</span><span class='varop'>.</span><span class='varid'>hs</span>
 
  +
rahC.ataD tropmi
<span class='varid'>rahC</span><span class='varop'>.</span><span class='varid'>ataD</span> <span class='varid'>tropmi</span>
 
  +
ebyaM.ataD tropmi
<span class='varid'>ebyaM</span><span class='varop'>.</span><span class='varid'>ataD</span> <span class='varid'>tropmi</span>
 
  +
tsiL.ataD tropmi
<span class='varid'>tsiL</span><span class='varop'>.</span><span class='varid'>ataD</span> <span class='varid'>tropmi</span>
 
</pre>
 
 
   
 
So we take the input string, split it into lines, and the loop over that
 
So we take the input string, split it into lines, and the loop over that
Line 292: Line 277:
 
syntax, and has the type:
 
syntax, and has the type:
   
  +
<haskell>
<pre> <span class='varid'>map</span> <span class='keyglyph'>::</span> <span class='layout'>(</span><span class='varid'>a</span> <span class='keyglyph'>-&gt;</span> <span class='varid'>b</span><span class='layout'>)</span> <span class='keyglyph'>-&gt;</span> <span class='keyglyph'>[</span><span class='varid'>a</span><span class='keyglyph'>]</span> <span class='keyglyph'>-&gt;</span> <span class='keyglyph'>[</span><span class='varid'>b</span><span class='keyglyph'>]</span>
 
  +
map :: (a -> b) -> [a] -> [b]
</pre>
 
  +
</haskell>
 
   
 
That is, it takes some function, and a list, and applies that function
 
That is, it takes some function, and a list, and applies that function
Line 301: Line 286:
 
Just for reference, 'map' is implemented as:
 
Just for reference, 'map' is implemented as:
   
  +
<haskell>
<pre> <span class='varid'>map</span> <span class='keyword'>_</span> <span class='keyglyph'>[</span><span class='keyglyph'>]</span> <span class='keyglyph'>=</span> <span class='keyglyph'>[</span><span class='keyglyph'>]</span>
 
  +
map _ [] = []
<span class='varid'>map</span> <span class='varid'>f</span> <span class='layout'>(</span><span class='varid'>x</span><span class='conop'>:</span><span class='varid'>xs</span><span class='layout'>)</span> <span class='keyglyph'>=</span> <span class='varid'>f</span> <span class='varid'>x</span> <span class='conop'>:</span> <span class='varid'>map</span> <span class='varid'>f</span> <span class='varid'>xs</span>
 
  +
map f (x:xs) = f x : map f xs
</pre>
 
  +
</haskell>
   
 
==File IO==
 
==File IO==
 
   
 
Operating on stdin/stdout is good for scripts (and this is how tools
 
Operating on stdin/stdout is good for scripts (and this is how tools
Line 312: Line 297:
 
to do some file IO. The basic operations of files are:
 
to do some file IO. The basic operations of files are:
   
  +
<haskell>
<pre> <span class='varid'>readFile</span> <span class='keyglyph'>::</span> <span class='conid'>FilePath</span> <span class='keyglyph'>-&gt;</span> <span class='conid'>IO</span> <span class='conid'>String</span>
 
  +
readFile :: FilePath -> IO String
<span class='varid'>writeFile</span> <span class='keyglyph'>::</span> <span class='conid'>FilePath</span> <span class='keyglyph'>-&gt;</span> <span class='conid'>String</span> <span class='keyglyph'>-&gt;</span> <span class='conid'>IO</span> <span class='layout'>(</span><span class='layout'>)</span>
 
  +
writeFile :: FilePath -> String -> IO ()
</pre>
 
  +
</haskell>
 
   
 
'readFile' takes a file name as an argument, does some IO, and returns the
 
'readFile' takes a file name as an argument, does some IO, and returns the
Line 325: Line 310:
 
We could implement a 'cp' program on files, as:
 
We could implement a 'cp' program on files, as:
   
  +
<haskell>
<pre> <span class='keyword'>import</span> <span class='conid'>System</span><span class='varop'>.</span><span class='conid'>Environment</span>
 
  +
import System.Environment
 
<span class='varid'>main</span> <span class='keyglyph'>=</span> <span class='keyword'>do</span>
 
<span class='keyglyph'>[</span><span class='varid'>f</span><span class='layout'>,</span><span class='varid'>g</span><span class='keyglyph'>]</span> <span class='keyglyph'>&lt;-</span> <span class='varid'>getArgs</span>
 
<span class='varid'>s</span> <span class='keyglyph'>&lt;-</span> <span class='varid'>readFile</span> <span class='varid'>f</span>
 
<span class='varid'>writeFile</span> <span class='varid'>g</span> <span class='varid'>s</span>
 
</pre>
 
   
  +
main = do
  +
[f,g] <- getArgs
  +
s <- readFile f
  +
writeFile g s
  +
</haskell>
   
 
Running this program:
 
Running this program:
   
  +
$ ghc -O A.hs
<pre> <span class='varop'>$</span> <span class='varid'>ghc</span> <span class='comment'>-</span><span class='conid'>O</span> <span class='conid'>A</span><span class='varop'>.</span><span class='varid'>hs</span>
 
   
  +
$ ./a.out A.hs Z.hs
<span class='varop'>$</span> <span class='varop'>./</span><span class='varid'>a</span><span class='varop'>.</span><span class='varid'>out</span> <span class='conid'>A</span><span class='varop'>.</span><span class='varid'>hs</span> <span class='conid'>Z</span><span class='varop'>.</span><span class='varid'>hs</span>
 
   
  +
$ cat Z.hs
<span class='varop'>$</span> <span class='varid'>cat</span> <span class='conid'>Z</span><span class='varop'>.</span><span class='varid'>hs</span>
 
<span class='keyword'>import</span> <span class='conid'>System</span><span class='varop'>.</span><span class='conid'>Environment</span>
+
import System.Environment
 
<span class='varid'>main</span> <span class='keyglyph'>=</span> <span class='keyword'>do</span>
 
<span class='keyglyph'>[</span><span class='varid'>f</span><span class='layout'>,</span><span class='varid'>g</span><span class='keyglyph'>]</span> <span class='keyglyph'>&lt;-</span> <span class='varid'>getArgs</span>
 
<span class='varid'>s</span> <span class='keyglyph'>&lt;-</span> <span class='varid'>readFile</span> <span class='varid'>f</span>
 
<span class='varid'>writeFile</span> <span class='varid'>g</span> <span class='varid'>s</span>
 
</pre>
 
   
  +
main = do
  +
[f,g] <- getArgs
  +
s <- readFile f
  +
writeFile g s
   
 
Since we're doing IO (the type of readFile and writeFile enforce this),
 
Since we're doing IO (the type of readFile and writeFile enforce this),
Line 363: Line 346:
 
effect, and save the result to a variable, you would write:
 
effect, and save the result to a variable, you would write:
   
  +
<haskell>
<pre> <span class='varid'>v</span> <span class='keyglyph'>&lt;-</span> <span class='varid'>action</span>
 
  +
v <- action
</pre>
 
  +
</haskell>
 
   
 
For example, to run the 'readFile' action, which has the side effect of
 
For example, to run the 'readFile' action, which has the side effect of
 
reading a file from disk, we say:
 
reading a file from disk, we say:
   
  +
<haskell>
<pre> <span class='varid'>s</span> <span class='keyglyph'>&lt;-</span> <span class='varid'>readFile</span> <span class='varid'>f</span>
 
  +
s <- readFile f
</pre>
 
  +
</haskell>
 
   
 
Finally, we can use the 'appendFile' function to append to an existing
 
Finally, we can use the 'appendFile' function to append to an existing
Line 389: Line 372:
 
To open up a file, and get its Handle, we use:
 
To open up a file, and get its Handle, we use:
   
  +
<haskell>
<pre> <span class='varid'>openFile</span> <span class='keyglyph'>::</span> <span class='conid'>FilePath</span> <span class='keyglyph'>-&gt;</span> <span class='conid'>IOMode</span> <span class='keyglyph'>-&gt;</span> <span class='conid'>IO</span> <span class='conid'>Handle</span>
 
  +
openFile :: FilePath -> IOMode -> IO Handle
</pre>
 
  +
</haskell>
 
   
 
So to open a file for reading only, in GHCi:
 
So to open a file for reading only, in GHCi:
   
  +
<haskell>
<pre> <span class='conid'>Prelude</span> <span class='conid'>System</span><span class='varop'>.</span><span class='conid'>IO</span><span class='varop'>&gt;</span> <span class='varid'>h</span> <span class='keyglyph'>&lt;-</span> <span class='varid'>openFile</span> <span class='str'>"A.hs"</span> <span class='conid'>ReadMode</span>
 
  +
Prelude System.IO> h <- openFile "A.hs" ReadMode
<span class='layout'>{</span><span class='varid'>handle</span><span class='conop'>:</span> <span class='conid'>A</span><span class='varop'>.</span><span class='varid'>hs</span><span class='layout'>}</span>
 
  +
{handle: A.hs}
</pre>
 
  +
</haskell>
 
   
 
Which returns a Handle onto the file "A.hs". We can read a line from this handle:
 
Which returns a Handle onto the file "A.hs". We can read a line from this handle:
   
  +
<haskell>
<pre> <span class='conid'>Prelude</span> <span class='conid'>System</span><span class='varop'>.</span><span class='conid'>IO</span><span class='varop'>&gt;</span> <span class='varid'>hGetLine</span> <span class='varid'>h</span>
 
  +
Prelude System.IO> hGetLine h
<span class='str'>"main = do"</span>
 
  +
"main = do"
</pre>
 
  +
</haskell>
 
   
 
To close a Handle, and flush the buffer:
 
To close a Handle, and flush the buffer:
   
  +
<haskell>
<pre> <span class='varid'>hClose</span> <span class='keyglyph'>::</span> <span class='conid'>Handle</span> <span class='keyglyph'>-&gt;</span> <span class='conid'>IO</span> <span class='layout'>(</span><span class='layout'>)</span>
 
  +
hClose :: Handle -> IO ()
</pre>
 
  +
</haskell>
 
   
 
Once a Handle is closed, we can no longer read from it:
 
Once a Handle is closed, we can no longer read from it:
   
  +
<haskell>
<pre> <span class='conid'>Prelude</span> <span class='conid'>System</span><span class='varop'>.</span><span class='conid'>IO</span><span class='varop'>&gt;</span> <span class='varid'>hClose</span> <span class='varid'>h</span>
 
  +
Prelude System.IO> hClose h
<span class='conid'>Prelude</span> <span class='conid'>System</span><span class='varop'>.</span><span class='conid'>IO</span><span class='varop'>&gt;</span> <span class='varid'>hGetLine</span> <span class='varid'>h</span>
 
  +
Prelude System.IO> hGetLine h
<span class='varop'>***</span> <span class='conid'>Exception</span><span class='conop'>:</span> <span class='conid'>A</span><span class='varop'>.</span><span class='varid'>hs</span><span class='conop'>:</span> <span class='varid'>hGetLine</span><span class='conop'>:</span> <span class='varid'>illegal</span> <span class='varid'>operation</span> <span class='layout'>(</span><span class='varid'>handle</span> <span class='varid'>is</span> <span class='varid'>closed</span><span class='layout'>)</span>
 
  +
*** Exception: A.hs: hGetLine: illegal operation (handle is closed)
</pre>
 
  +
</haskell>
 
   
 
We can also flush explicitly with:
 
We can also flush explicitly with:
   
  +
<haskell>
<pre> <span class='varid'>hFlush</span> <span class='keyglyph'>::</span> <span class='conid'>Handle</span> <span class='keyglyph'>-&gt;</span> <span class='conid'>IO</span> <span class='layout'>(</span><span class='layout'>)</span>
 
  +
hFlush :: Handle -> IO ()
</pre>
 
  +
</haskell>
 
   
 
Other useful operations for reading from Handles:
 
Other useful operations for reading from Handles:
   
  +
<haskell>
<pre> <span class='varid'>hGetChar</span> <span class='keyglyph'>::</span> <span class='conid'>Handle</span> <span class='keyglyph'>-&gt;</span> <span class='conid'>IO</span> <span class='conid'>Char</span>
 
  +
hGetChar :: Handle -> IO Char
<span class='varid'>hGetLine</span> <span class='keyglyph'>::</span> <span class='conid'>Handle</span> <span class='keyglyph'>-&gt;</span> <span class='conid'>IO</span> <span class='keyglyph'>[</span><span class='conid'>Char</span><span class='keyglyph'>]</span>
 
  +
hGetLine :: Handle -> IO [Char]
<span class='varid'>hGetContents</span> <span class='keyglyph'>::</span> <span class='conid'>Handle</span> <span class='keyglyph'>-&gt;</span> <span class='conid'>IO</span> <span class='keyglyph'>[</span><span class='conid'>Char</span><span class='keyglyph'>]</span>
 
  +
hGetContents :: Handle -> IO [Char]
</pre>
 
  +
</haskell>
 
   
 
To write to Handles:
 
To write to Handles:
   
  +
<haskell>
<pre> <span class='varid'>hPutChar</span> <span class='keyglyph'>::</span> <span class='conid'>Handle</span> <span class='keyglyph'>-&gt;</span> <span class='conid'>Char</span> <span class='keyglyph'>-&gt;</span> <span class='conid'>IO</span> <span class='layout'>(</span><span class='layout'>)</span>
 
  +
hPutChar :: Handle -> Char -> IO ()
<span class='varid'>hPutStr</span> <span class='keyglyph'>::</span> <span class='conid'>Handle</span> <span class='keyglyph'>-&gt;</span> <span class='keyglyph'>[</span><span class='conid'>Char</span><span class='keyglyph'>]</span> <span class='keyglyph'>-&gt;</span> <span class='conid'>IO</span> <span class='layout'>(</span><span class='layout'>)</span>
 
  +
hPutStr :: Handle -> [Char] -> IO ()
<span class='varid'>hPutStrLn</span> <span class='keyglyph'>::</span> <span class='conid'>Handle</span> <span class='keyglyph'>-&gt;</span> <span class='keyglyph'>[</span><span class='conid'>Char</span><span class='keyglyph'>]</span> <span class='keyglyph'>-&gt;</span> <span class='conid'>IO</span> <span class='layout'>(</span><span class='layout'>)</span>
 
  +
hPutStrLn :: Handle -> [Char] -> IO ()
<span class='varid'>hPrint</span> <span class='keyglyph'>::</span> <span class='conid'>Show</span> <span class='varid'>a</span> <span class='keyglyph'>=&gt;</span> <span class='conid'>Handle</span> <span class='keyglyph'>-&gt;</span> <span class='varid'>a</span> <span class='keyglyph'>-&gt;</span> <span class='conid'>IO</span> <span class='layout'>(</span><span class='layout'>)</span>
 
  +
hPrint :: Show a => Handle -> a -> IO ()
</pre>
 
  +
</haskell>
 
   
 
Some other useful actions:
 
Some other useful actions:
   
  +
<haskell>
<pre> <span class='varid'>hSeek</span> <span class='keyglyph'>::</span> <span class='conid'>Handle</span> <span class='keyglyph'>-&gt;</span> <span class='conid'>SeekMode</span> <span class='keyglyph'>-&gt;</span> <span class='conid'>Integer</span> <span class='keyglyph'>-&gt;</span> <span class='conid'>IO</span> <span class='layout'>(</span><span class='layout'>)</span>
 
  +
hSeek :: Handle -> SeekMode -> Integer -> IO ()
<span class='varid'>hTell</span> <span class='keyglyph'>::</span> <span class='conid'>Handle</span> <span class='keyglyph'>-&gt;</span> <span class='conid'>IO</span> <span class='conid'>Integer</span>
 
  +
hTell :: Handle -> IO Integer
<span class='varid'>hFileSize</span> <span class='keyglyph'>::</span> <span class='conid'>Handle</span> <span class='keyglyph'>-&gt;</span> <span class='conid'>IO</span> <span class='conid'>Integer</span>
 
  +
hFileSize :: Handle -> IO Integer
<span class='varid'>hIsEOF</span> <span class='keyglyph'>::</span> <span class='conid'>Handle</span> <span class='keyglyph'>-&gt;</span> <span class='conid'>IO</span> <span class='conid'>Bool</span>
 
  +
hIsEOF :: Handle -> IO Bool
</pre>
 
  +
</haskell>
   
 
==An example: spell checking==
 
==An example: spell checking==
Line 460: Line 444:
 
dictionary in a Set data type. First, some libraries to import:
 
dictionary in a Set data type. First, some libraries to import:
   
  +
<haskell>
<pre> <span class='keyword'>import</span> <span class='conid'>System</span><span class='varop'>.</span><span class='conid'>Environment</span>
 
  +
import System.Environment
<span class='keyword'>import</span> <span class='conid'>Control</span><span class='varop'>.</span><span class='conid'>Monad</span>
 
  +
import Control.Monad
<span class='keyword'>import</span> <span class='conid'>Data</span><span class='varop'>.</span><span class='conid'>Set</span>
 
  +
import Data.Set
</pre>
 
  +
</haskell>
 
   
 
And the complete program:
 
And the complete program:
   
  +
<haskell>
<pre> <span class='varid'>main</span> <span class='keyglyph'>=</span> <span class='keyword'>do</span>
 
  +
main = do
<span class='keyglyph'>[</span><span class='varid'>s</span><span class='keyglyph'>]</span> <span class='keyglyph'>&lt;-</span> <span class='varid'>getArgs</span>
 
  +
[s] <- getArgs
<span class='varid'>f</span> <span class='keyglyph'>&lt;-</span> <span class='varid'>readFile</span> <span class='str'>"/usr/share/dict/words"</span>
 
  +
f <- readFile "/usr/share/dict/words"
<span class='varid'>g</span> <span class='keyglyph'>&lt;-</span> <span class='varid'>readFile</span> <span class='varid'>s</span>
 
  +
g <- readFile s
<span class='keyword'>let</span> <span class='varid'>dict</span> <span class='keyglyph'>=</span> <span class='varid'>fromList</span> <span class='layout'>(</span><span class='varid'>lines</span> <span class='varid'>f</span><span class='layout'>)</span>
 
  +
let dict = fromList (lines f)
<span class='varid'>mapM_</span> <span class='layout'>(</span><span class='varid'>spell</span> <span class='varid'>dict</span><span class='layout'>)</span> <span class='layout'>(</span><span class='varid'>words</span> <span class='varid'>g</span><span class='layout'>)</span>
 
  +
mapM_ (spell dict) (words g)
 
<span class='varid'>spell</span> <span class='varid'>d</span> <span class='varid'>w</span> <span class='keyglyph'>=</span> <span class='varid'>when</span> <span class='layout'>(</span><span class='varid'>w</span> <span class='varop'>`notMember`</span> <span class='varid'>d</span><span class='layout'>)</span> <span class='layout'>(</span><span class='varid'>putStrLn</span> <span class='varid'>w</span><span class='layout'>)</span>
 
</pre>
 
   
  +
spell d w = when (w `notMember` d) (putStrLn w)
  +
</haskell>
   
 
Running this program, on its own source, and it reports the following
 
Running this program, on its own source, and it reports the following
 
words are not found in the dictionary:
 
words are not found in the dictionary:
   
  +
$ ghc -O Spell.hs -o spell
<pre> <span class='varop'>$</span> <span class='varid'>ghc</span> <span class='comment'>-</span><span class='conid'>O</span> <span class='conid'>Spell</span><span class='varop'>.</span><span class='varid'>hs</span> <span class='comment'>-</span><span class='varid'>o</span> <span class='varid'>spell</span>
 
  +
  +
$ ./spell A.hs
  +
Data.Char
  +
=
  +
<-
  +
(map
  +
toUpper
  +
n)
  +
=
  +
<-
  +
getLine
  +
1
   
<span class='varop'>$</span> <span class='varop'>./</span><span class='varid'>spell</span> <span class='conid'>A</span><span class='varop'>.</span><span class='varid'>hs</span>
 
<span class='conid'>Data</span><span class='varop'>.</span><span class='conid'>Char</span>
 
<span class='keyglyph'>=</span>
 
<span class='keyglyph'>&lt;-</span>
 
<span class='layout'>(</span><span class='varid'>map</span>
 
<span class='varid'>toUpper</span>
 
<span class='varid'>n</span><span class='layout'>)</span>
 
<span class='keyglyph'>=</span>
 
<span class='keyglyph'>&lt;-</span>
 
<span class='varid'>getLine</span>
 
<span class='num'>1</span>
 
</pre>
 
   
 
===Writing the results out===
 
===Writing the results out===
Line 503: Line 487:
 
so. Let's import a couple of other modules:
 
so. Let's import a couple of other modules:
   
  +
<haskell>
<pre> <span class='keyword'>import</span> <span class='conid'>Data</span><span class='varop'>.</span><span class='conid'>Set</span>
 
  +
import Data.Set
<span class='keyword'>import</span> <span class='conid'>Data</span><span class='varop'>.</span><span class='conid'>Maybe</span>
 
  +
import Data.Maybe
<span class='keyword'>import</span> <span class='conid'>Text</span><span class='varop'>.</span><span class='conid'>Printf</span>
 
  +
import Text.Printf
<span class='keyword'>import</span> <span class='conid'>System</span><span class='varop'>.</span><span class='conid'>IO</span>
 
  +
import System.IO
<span class='keyword'>import</span> <span class='conid'>System</span><span class='varop'>.</span><span class='conid'>Environment</span>
 
  +
import System.Environment
<span class='keyword'>import</span> <span class='conid'>System</span><span class='varop'>.</span><span class='conid'>Posix</span><span class='varop'>.</span><span class='conid'>Temp</span>
 
  +
import System.Posix.Temp
</pre>
 
  +
</haskell>
 
   
 
Refactoring the main code to separate out the reading and writing phases
 
Refactoring the main code to separate out the reading and writing phases
 
in to their own function, we end up with the core code:
 
in to their own function, we end up with the core code:
   
  +
<haskell>
<pre> <span class='varid'>main</span> <span class='keyglyph'>=</span> <span class='keyword'>do</span>
 
  +
main = do
<span class='layout'>(</span><span class='varid'>f</span><span class='layout'>,</span> <span class='varid'>g</span><span class='layout'>)</span> <span class='keyglyph'>&lt;-</span> <span class='varid'>readFiles</span>
 
  +
(f, g) <- readFiles
<span class='keyword'>let</span> <span class='varid'>dict</span> <span class='keyglyph'>=</span> <span class='varid'>fromList</span> <span class='layout'>(</span><span class='varid'>lines</span> <span class='varid'>f</span><span class='layout'>)</span>
 
  +
let dict = fromList (lines f)
<span class='varid'>errs</span> <span class='keyglyph'>=</span> <span class='varid'>mapMaybe</span> <span class='layout'>(</span><span class='varid'>spell</span> <span class='varid'>dict</span><span class='layout'>)</span> <span class='layout'>(</span><span class='varid'>words</span> <span class='varid'>g</span><span class='layout'>)</span>
 
  +
errs = mapMaybe (spell dict) (words g)
<span class='varid'>write</span> <span class='varid'>errs</span>
 
  +
write errs
 
<span class='varid'>spell</span> <span class='varid'>d</span> <span class='varid'>w</span> <span class='keyglyph'>|</span> <span class='varid'>w</span> <span class='varop'>`notMember`</span> <span class='varid'>d</span> <span class='keyglyph'>=</span> <span class='conid'>Just</span> <span class='varid'>w</span>
 
<span class='keyglyph'>|</span> <span class='varid'>otherwise</span> <span class='keyglyph'>=</span> <span class='conid'>Nothing</span>
 
</pre>
 
   
  +
spell d w | w `notMember` d = Just w
  +
| otherwise = Nothing
  +
</haskell>
   
 
Where reading is now its own function:
 
Where reading is now its own function:
   
  +
<haskell>
<pre> <span class='varid'>readFiles</span> <span class='keyglyph'>=</span> <span class='keyword'>do</span>
 
  +
readFiles = do
<span class='keyglyph'>[</span><span class='varid'>s</span><span class='keyglyph'>]</span> <span class='keyglyph'>&lt;-</span> <span class='varid'>getArgs</span>
 
  +
[s] <- getArgs
<span class='varid'>f</span> <span class='keyglyph'>&lt;-</span> <span class='varid'>readFile</span> <span class='str'>"/usr/share/dict/words"</span>
 
  +
f <- readFile "/usr/share/dict/words"
<span class='varid'>g</span> <span class='keyglyph'>&lt;-</span> <span class='varid'>readFile</span> <span class='varid'>s</span>
 
  +
g <- readFile s
<span class='varid'>return</span> <span class='layout'>(</span><span class='varid'>f</span><span class='layout'>,</span><span class='varid'>g</span><span class='layout'>)</span>
 
  +
return (f,g)
</pre>
 
  +
</haskell>
 
   
 
And writing errors out to their own file:
 
And writing errors out to their own file:
   
  +
<haskell>
<pre> <span class='varid'>write</span> <span class='varid'>errs</span> <span class='keyglyph'>=</span> <span class='keyword'>do</span>
 
  +
write errs = do
<span class='layout'>(</span><span class='varid'>t</span><span class='layout'>,</span><span class='varid'>h</span><span class='layout'>)</span> <span class='keyglyph'>&lt;-</span> <span class='varid'>mkstemp</span> <span class='str'>"/tmp/spell.XXXXXX"</span>
 
  +
(t,h) <- mkstemp "/tmp/spell.XXXXXX"
<span class='varid'>mapM_</span> <span class='layout'>(</span><span class='varid'>hPutStrLn</span> <span class='varid'>h</span><span class='layout'>)</span> <span class='varid'>errs</span>
 
  +
mapM_ (hPutStrLn h) errs
<span class='varid'>hClose</span> <span class='varid'>h</span>
 
  +
hClose h
<span class='varid'>printf</span> <span class='str'>"%d spelling errors written to '%s'\n"</span> <span class='layout'>(</span><span class='varid'>length</span> <span class='varid'>errs</span><span class='layout'>)</span> <span class='varid'>t</span>
 
  +
printf "%d spelling errors written to '%s'\n" (length errs) t
</pre>
 
  +
</haskell>
   
 
Pretty simple! Running this program:
 
Pretty simple! Running this program:
   
<pre> <span class='varop'>$</span> <span class='varid'>ghc</span> <span class='comment'>--make -O Spell.hs -o myspell</span>
+
$ ghc --make -O Spell.hs -o myspell
  +
[1 of 1] Compiling Main ( Spell.hs, Spell.o )
<span class='keyglyph'>[</span><span class='num'>1</span> <span class='keyword'>of</span> <span class='num'>1</span><span class='keyglyph'>]</span> <span class='conid'>Compiling</span> <span class='conid'>Main</span> <span class='layout'>(</span> <span class='conid'>Spell</span><span class='varop'>.</span><span class='varid'>hs</span><span class='layout'>,</span> <span class='conid'>Spell</span><span class='varop'>.</span><span class='varid'>o</span> <span class='layout'>)</span>
 
<span class='conid'>Linking</span> <span class='varid'>myspell</span> <span class='varop'>...</span>
+
Linking myspell ...
   
  +
$ ./myspell Spell.hs
<span class='varop'>$</span> <span class='varop'>./</span><span class='varid'>myspell</span> <span class='conid'>Spell</span><span class='varop'>.</span><span class='varid'>hs</span>
 
  +
67 spelling errors written to '/tmp/spell.ia8256'
<span class='num'>67</span> <span class='varid'>spelling</span> <span class='varid'>errors</span> <span class='varid'>written</span> <span class='varid'>to</span> <span class='chr'>'</span><span class='varop'>/</span><span class='varid'>tmp</span><span class='varop'>/</span><span class='varid'>spell</span><span class='varop'>.</span><span class='varid'>ia8256'</span>
 
</pre>
 
   
 
==Extension: using SMP parallelism==
 
==Extension: using SMP parallelism==
Line 573: Line 557:
 
Here's the source, for you to ponder. First some imports:
 
Here's the source, for you to ponder. First some imports:
   
  +
<haskell>
<pre> <span class='keyword'>import</span> <span class='conid'>Data</span><span class='varop'>.</span><span class='conid'>Set</span> <span class='varid'>hiding</span> <span class='layout'>(</span><span class='varid'>map</span><span class='layout'>)</span>
 
  +
import Data.Set hiding (map)
<span class='keyword'>import</span> <span class='conid'>Data</span><span class='varop'>.</span><span class='conid'>Maybe</span>
 
  +
import Data.Maybe
<span class='keyword'>import</span> <span class='conid'>Data</span><span class='varop'>.</span><span class='conid'>Char</span>
 
  +
import Data.Char
<span class='keyword'>import</span> <span class='conid'>Text</span><span class='varop'>.</span><span class='conid'>Printf</span>
 
  +
import Text.Printf
<span class='keyword'>import</span> <span class='conid'>System</span><span class='varop'>.</span><span class='conid'>IO</span>
 
  +
import System.IO
<span class='keyword'>import</span> <span class='conid'>System</span><span class='varop'>.</span><span class='conid'>Environment</span>
 
  +
import System.Environment
<span class='keyword'>import</span> <span class='conid'>Control</span><span class='varop'>.</span><span class='conid'>Concurrent</span>
 
  +
import Control.Concurrent
<span class='keyword'>import</span> <span class='conid'>Control</span><span class='varop'>.</span><span class='conid'>Monad</span>
 
  +
import Control.Monad
</pre>
 
  +
</haskell>
 
   
 
The entry point, modified to break the word list into chunks, and then
 
The entry point, modified to break the word list into chunks, and then
 
dispatching a chunk to each thread:
 
dispatching a chunk to each thread:
   
  +
<haskell>
<pre> <span class='varid'>main</span> <span class='keyglyph'>=</span> <span class='keyword'>do</span>
 
  +
main = do
<span class='layout'>(</span><span class='varid'>f</span><span class='layout'>,</span> <span class='varid'>g</span><span class='layout'>,</span> <span class='varid'>n</span><span class='layout'>)</span> <span class='keyglyph'>&lt;-</span> <span class='varid'>readFiles</span>
 
  +
(f, g, n) <- readFiles
<span class='keyword'>let</span> <span class='varid'>dict</span> <span class='keyglyph'>=</span> <span class='varid'>fromList</span> <span class='layout'>(</span><span class='varid'>lines</span> <span class='varid'>f</span><span class='layout'>)</span>
 
  +
let dict = fromList (lines f)
<span class='varid'>work</span> <span class='keyglyph'>=</span> <span class='varid'>chunk</span> <span class='varid'>n</span> <span class='layout'>(</span><span class='varid'>words</span> <span class='varid'>g</span><span class='layout'>)</span>
 
  +
work = chunk n (words g)
<span class='varid'>run</span> <span class='varid'>n</span> <span class='varid'>dict</span> <span class='varid'>work</span>
 
  +
run n dict work
</pre>
 
  +
</haskell>
 
   
 
The 'run' function sets up a channel between the main thread and all
 
The 'run' function sets up a channel between the main thread and all
Line 600: Line 584:
 
each piece of the work list:
 
each piece of the work list:
   
  +
<haskell>
<pre> <span class='varid'>run</span> <span class='varid'>n</span> <span class='varid'>dict</span> <span class='varid'>work</span> <span class='keyglyph'>=</span> <span class='keyword'>do</span>
 
  +
run n dict work = do
<span class='varid'>chan</span> <span class='keyglyph'>&lt;-</span> <span class='varid'>newChan</span>
 
  +
chan <- newChan
<span class='varid'>errs</span> <span class='keyglyph'>&lt;-</span> <span class='varid'>getChanContents</span> <span class='varid'>chan</span> <span class='comment'>-- errors returned back to main thread</span>
 
  +
errs <- getChanContents chan -- errors returned back to main thread
<span class='varid'>mapM_</span> <span class='layout'>(</span><span class='varid'>forkIO</span> <span class='varop'>.</span> <span class='varid'>thread</span> <span class='varid'>chan</span> <span class='varid'>dict</span><span class='layout'>)</span> <span class='layout'>(</span><span class='varid'>zip</span> <span class='keyglyph'>[</span><span class='num'>1</span><span class='keyglyph'>..</span><span class='varid'>n</span><span class='keyglyph'>]</span> <span class='varid'>work</span><span class='layout'>)</span>
 
  +
mapM_ (forkIO . thread chan dict) (zip [1..n] work)
<span class='varid'>wait</span> <span class='varid'>n</span> <span class='varid'>errs</span> <span class='num'>0</span>
 
  +
wait n errs 0
</pre>
 
  +
</haskell>
   
   
Line 611: Line 596:
 
any spelling errors they pass up:
 
any spelling errors they pass up:
   
  +
<haskell>
<pre> <span class='varid'>wait</span> <span class='varid'>n</span> <span class='varid'>xs</span> <span class='varid'>i</span> <span class='keyglyph'>=</span> <span class='varid'>when</span> <span class='layout'>(</span><span class='varid'>i</span> <span class='varop'>&lt;</span> <span class='varid'>n</span><span class='layout'>)</span> <span class='varop'>$</span> <span class='keyword'>case</span> <span class='varid'>xs</span> <span class='keyword'>of</span>
 
  +
wait n xs i = when (i < n) $ case xs of
<span class='conid'>Nothing</span> <span class='conop'>:</span> <span class='varid'>ys</span> <span class='keyglyph'>-&gt;</span> <span class='varid'>wait</span> <span class='varid'>n</span> <span class='varid'>ys</span> <span class='varop'>$!</span> <span class='varid'>i</span><span class='varop'>+</span><span class='num'>1</span>
 
  +
Nothing : ys -> wait n ys $! i+1
<span class='conid'>Just</span> <span class='varid'>s</span> <span class='conop'>:</span> <span class='varid'>ys</span> <span class='keyglyph'>-&gt;</span> <span class='varid'>putStrLn</span> <span class='varid'>s</span> <span class='varop'>&gt;&gt;</span> <span class='varid'>wait</span> <span class='varid'>n</span> <span class='varid'>ys</span> <span class='varid'>i</span>
 
  +
Just s : ys -> putStrLn s >> wait n ys i
</pre>
 
  +
</haskell>
 
   
 
Each thread spell checks its own piece of the work list. If it finds a
 
Each thread spell checks its own piece of the work list. If it finds a
Line 621: Line 606:
 
the main thread.
 
the main thread.
   
  +
<haskell>
<pre> <span class='varid'>thread</span> <span class='varid'>chan</span> <span class='varid'>dict</span> <span class='layout'>(</span><span class='varid'>me</span><span class='layout'>,</span> <span class='varid'>xs</span><span class='layout'>)</span> <span class='keyglyph'>=</span> <span class='keyword'>do</span>
 
  +
thread chan dict (me, xs) = do
<span class='varid'>mapM_</span> <span class='varid'>spellit</span> <span class='varid'>xs</span>
 
  +
mapM_ spellit xs
<span class='varid'>writeChan</span> <span class='varid'>chan</span> <span class='conid'>Nothing</span>
 
  +
writeChan chan Nothing
 
<span class='keyword'>where</span>
 
<span class='varid'>spellit</span> <span class='varid'>w</span> <span class='keyglyph'>=</span> <span class='keyword'>do</span>
 
<span class='varid'>when</span> <span class='layout'>(</span><span class='varid'>spell</span> <span class='varid'>dict</span> <span class='varid'>w</span><span class='layout'>)</span> <span class='varop'>$</span>
 
<span class='varid'>writeChan</span> <span class='varid'>chan</span> <span class='varop'>.</span> <span class='conid'>Just</span> <span class='varop'>$</span> <span class='varid'>printf</span> <span class='str'>"Thread %d: %-25s"</span> <span class='layout'>(</span><span class='varid'>me</span><span class='keyglyph'>::</span><span class='conid'>Int</span><span class='layout'>)</span> <span class='varid'>w</span>
 
</pre>
 
   
  +
where
  +
spellit w = when (spell dict w) $
  +
writeChan chan . Just $ printf "Thread %d: %-25s" (me::Int) w
  +
</haskell>
   
 
The 'spell' function is simplified a little:
 
The 'spell' function is simplified a little:
   
  +
<haskell>
<pre> <span class='varid'>spell</span> <span class='varid'>d</span> <span class='varid'>w</span> <span class='keyglyph'>=</span> <span class='varid'>w</span> <span class='varop'>`notMember`</span> <span class='varid'>d</span>
 
  +
spell d w = w `notMember` d
</pre>
 
  +
</haskell>
   
 
which we could also write as:
 
which we could also write as:
   
  +
<haskell>
<pre> <span class='varid'>spell</span> <span class='keyglyph'>=</span> <span class='varid'>flip</span> <span class='varid'>notMember</span>
 
  +
spell = flip notMember
</pre>
 
  +
</haskell>
 
   
 
We modify the readFiles phase to take an additional numeric command line
 
We modify the readFiles phase to take an additional numeric command line
 
argument, specifying the number of threads to run:
 
argument, specifying the number of threads to run:
   
  +
<haskell>
<pre> <span class='varid'>readFiles</span> <span class='keyglyph'>=</span> <span class='keyword'>do</span>
 
  +
readFiles = do
<span class='keyglyph'>[</span><span class='varid'>s</span><span class='layout'>,</span><span class='varid'>n</span><span class='keyglyph'>]</span> <span class='keyglyph'>&lt;-</span> <span class='varid'>getArgs</span>
 
  +
[s,n] <- getArgs
<span class='varid'>f</span> <span class='keyglyph'>&lt;-</span> <span class='varid'>readFile</span> <span class='str'>"/usr/share/dict/words"</span>
 
  +
f <- readFile "/usr/share/dict/words"
<span class='varid'>g</span> <span class='keyglyph'>&lt;-</span> <span class='varid'>readFile</span> <span class='varid'>s</span>
 
  +
g <- readFile s
<span class='varid'>return</span> <span class='layout'>(</span><span class='varid'>f</span><span class='layout'>,</span><span class='varid'>g</span><span class='layout'>,</span> <span class='varid'>read</span> <span class='varid'>n</span><span class='layout'>)</span>
 
  +
return (f,g, read n)
</pre>
 
  +
</haskell>
 
   
 
We compile this with the GHC SMP parallel runtime system:
 
We compile this with the GHC SMP parallel runtime system:
   
<pre> <span class='varop'>$</span> <span class='varid'>ghc</span> <span class='comment'>-</span><span class='conid'>O</span> <span class='comment'>--make -threaded Spell.hs -o spell</span>
+
$ ghc -O --make -threaded Spell.hs -o spell
</pre>
 
 
   
 
Now, we can run 'n' worker threads (lightweight Haskell threads), mapped
 
Now, we can run 'n' worker threads (lightweight Haskell threads), mapped
Line 664: Line 647:
 
around with 4 OS threads. First, running everything in a single thread:
 
around with 4 OS threads. First, running everything in a single thread:
   
  +
$ time ./spell test.txt 1 +RTS -N1
<pre> <span class='varop'>$</span> <span class='varid'>time</span> <span class='varop'>./</span><span class='varid'>spell</span> <span class='varid'>test</span><span class='varop'>.</span><span class='varid'>txt</span> <span class='num'>1</span> <span class='varop'>+</span><span class='conid'>RTS</span> <span class='comment'>-</span><span class='conid'>N1</span>
 
<span class='varop'>...</span>
+
...
  +
Thread 1: week:
<span class='conid'>Thread</span> <span class='num'>1</span><span class='conop'>:</span> <span class='varid'>week</span><span class='conop'>:</span>
 
  +
Thread 1: IO!
<span class='conid'>Thread</span> <span class='num'>1</span><span class='conop'>:</span> <span class='conid'>IO</span><span class='varop'>!</span>
 
  +
./spell test.txt 1 +RTS -N1 99% cpu 2.533 total
<span class='varop'>./</span><span class='varid'>spell</span> <span class='varid'>test</span><span class='varop'>.</span><span class='varid'>txt</span> <span class='num'>1</span> <span class='varop'>+</span><span class='conid'>RTS</span> <span class='comment'>-</span><span class='conid'>N1</span> <span class='num'>99</span><span class='varop'>%</span> <span class='varid'>cpu</span> <span class='num'>2.533</span> <span class='varid'>total</span>
 
</pre>
 
 
   
 
Ok, now we change the command line flag to run it with 4 OS threads, to
 
Ok, now we change the command line flag to run it with 4 OS threads, to
 
try to utilise all 4 cores:
 
try to utilise all 4 cores:
   
  +
$ time ./spell 4 +RTS -N4
<pre> <span class='varop'>$</span> <span class='varid'>time</span> <span class='varop'>./</span><span class='varid'>spell</span> <span class='num'>4</span> <span class='varop'>+</span><span class='conid'>RTS</span> <span class='comment'>-</span><span class='conid'>N4</span>
 
<span class='varop'>...</span>
+
...
  +
Thread 2: week:
<span class='conid'>Thread</span> <span class='num'>2</span><span class='conop'>:</span> <span class='varid'>week</span><span class='conop'>:</span>
 
  +
Thread 3: IO!
<span class='conid'>Thread</span> <span class='num'>3</span><span class='conop'>:</span> <span class='conid'>IO</span><span class='varop'>!</span>
 
  +
./spell test.txt 4 +RTS -N4 111% cpu 2.335 total
<span class='varop'>./</span><span class='varid'>spell</span> <span class='varid'>test</span><span class='varop'>.</span><span class='varid'>txt</span> <span class='num'>4</span> <span class='varop'>+</span><span class='conid'>RTS</span> <span class='comment'>-</span><span class='conid'>N4</span> <span class='num'>111</span><span class='varop'>%</span> <span class='varid'>cpu</span> <span class='num'>2.335</span> <span class='varid'>total</span>
 
</pre>
 
 
   
 
Ok. Good... A little bit faster, uses a little bit more cpu. It turns
 
Ok. Good... A little bit faster, uses a little bit more cpu. It turns

Revision as of 07:15, 17 December 2006

Programming Haskell: String processing (with a dash of concurrency)

This is part two in a series of tutorials on programming Haskell. You can get up to speed by reading yesterday's introductory article.

Today we'll look more into the basic tools at our disposal in the Haskell language, in particular, operations for doing IO and playing with files and strings.

Administrivia

Before we get started, I should clarify a small point raised by yesterday's article. One issue I forgot to mention was that there are slight differences between running Haskell in ghci, the bytecode interpreter, and compiling it to native code with GHC.

Haskell programs are executed by evaluating the special 'main' function.

    import Data.List

    mylength = foldr (const (+1)) 0
    main = print (mylength "haskell")

To compile this to native code, we would feed the source file to the compiler:

   $ ghc A.hs
   $ ./a.out
   7

For a faster turnaround, we can run the code directly through the bytecode interpreter, GHCi, using the 'runhaskell' program:

   $ runhaskell A.hs
   7

GHCi, the interactive Haskell environment, is a little bit different. As it is an interactive system, GHCi must execute your code sequentially, as you define each line. This is different to normal Haskell, where the order of definition is irrelevant. GHCi effectively executes your code inside a do-block. Therefore you can use the do-notation at the GHCi prompt to define new functions:

   $ ghci
   Prelude> :m + Data.List
   Prelude> let mylength = foldr (const (+1)) 0
   Prelude> :t mylength
   mylength :: [a] -> Integer
   Prelude> mylength "haskell"
   7

For this tutorial I will be developing code in a source file, and either compiling it as above, or loading the source file into GHCi for testing. To load a source file into GHCi, we do:

   $ ghci
   Prelude> :load A.hs
   *Main> :t main
   main :: IO ()
   *Main> :t mylength
   mylength :: [a] -> Integer
   *Main> mylength "foo"
   3
   *Main> main
   7

Now, let's get into the code!

IO

As the Camel Book says:

Unless you're using artificial intelligence to model a solipsistic philosopher, your program needs some way to communicate with the outside world.


In yesterday's tutorial, I briefly introduced 'readFile', for reading a String from a file on disk. Let's consider now IO in more detail. The most common IO operations are defined in the <a href="http://haskell.org/ghc/docs/latest/html/libraries/base/System-IO.html">System.IO</a> library.


For the most basic stdin/stdout Unix-style programs in Haskell, we can use the 'interact' function:

    interact    ::  (String -> String) -> IO ()

This higher order function takes, as an argument, some function for processing a string (of type String -> String). It runs this function over the standard input stream, printing the result to standard output. A surprisingly large number of useful programs can be written this way. For example, we can write the 'cat' unix program as:

    main = interact id

Yes, that's it! Let's compile and run this program:

   $ ghc -O A.hs
   $ cat A.hs | ./a.out
   main = interact id

How does this work? Firstly, 'interact' is defined as:

    interact f = do s <- getContents
                    putStr (f s)

So it reads a string from standard input, and writes to standard output the result of applying its argument function to that string. The 'id' function itself has the type:

    id :: a -> a

'id' is a function of one argument, of any type (the lowercase 'a' in the type means any type can be used in that position, i.e. it is a polymorphic function (also called a generic function in some languages)). 'id' takes a value of some type 'a', and returns a value of the same type. There's is only one (non-trivial) function of this type:

    id a = a

So 'interact id' will print to the input string to standard output unmodified.

Let's now write the 'wc' program:

    main    = interact count
    count s = show (length s) ++ "\n"

This will print the length of the input string, that is, the number of chars:

   $ runhaskell A.hs < A.hs
   57

Line oriented IO

Only a small number of programs operate on unstructured input streams. It is far more common to treat an input stream as a list of lines. So let's do that. To break a string up into lines, we'll use the ... 'lines' function, defined in the Data.List library:

    lines :: String -> [String]

The type, once again, tells the story. 'lines' takes a String, and breaks it up into a list of strings, splitting on newlines. To join a list of strings back into a single string, inserting newlines, we'd use the ... 'unlines' function:

    unlines :: [String] -> String

There are also similar functions for splitting on words, namely 'words' and 'unwords'. Now, an example. To count the number of lines in a file:

    main = interact (count . lines)

We can run this as:

   $ ghc -O A.hs
   $ ./a.out < A.hs
   3

Here we reuse the 'count' function from above, by composing it with the lines function.

On composition

This nice code reuse via composition is achieved using the (.) function, pronounced 'compose'. Let's look at how that works. (Feel free to skip this section, if you want to just get things done).


The (.) function is just a normal everyday Haskell function, defined as:

    (.) f g x = f (g x)

This looks a little like magic (or line noise), but its pretty easy. The (.) function simply takes two functions as arguments, along with another value. It applies the 'g' function to the value 'x', and then applies 'f' to the result, returning this final value. The functions may be of any type. The type of (.) is actually:

    (.) :: (b -> c) -> (a -> b) -> a -> c

which might look a bit hairy, but it essentially specifies what types of arguments make sense to compose. That is, only those where:

    f :: b -> c
    g :: a -> b
    x :: a

can be composed, yielding a new function of type:

    (f . g) :: a -> c

The nice thing is that this composition makes sense (and works) for all types a, b and c.


How does this relate to code reuse? Well, since our 'count' function is polymorphic, it works equally well counting the length of a string, or the length of a list of strings. Our littler 'wc' program is the epitome of the phrase: "higher order + polymorphic = reusable". That is, functions which take other functions as arguments, when combined with functions that work over any type, produce great reusable 'glue'. You only need vary the argument function to gain terrific code reuse (and the strong type checking ensures you can only reuse code in ways that work).

More on lines

Another little example, let's reverse each line of a file (like the unix 'rev' command):

    main = interact (unlines . map reverse . lines)

Which when run, reverses the input lines:

   $ ./a.out < B.hs
   rahC.ataD tropmi
   ebyaM.ataD tropmi
   tsiL.ataD tropmi

So we take the input string, split it into lines, and the loop over that list of lines, reversing each of them, using the 'map' function. Finally, once we've reversed each line, we join them back into a single string with unlines, and print it out.


The 'map' function is a fundamental control structure of functional programming, similar to the 'foreach' keyword in a number of imperative languages. 'map' however is just a function on lists, not built in syntax, and has the type:

    map :: (a -> b) -> [a] -> [b]

That is, it takes some function, and a list, and applies that function to each element of the list, returning a new list as a result. Since loops are so common in programming, we'll be using 'map' a lot. Just for reference, 'map' is implemented as:

    map _ []     = []
    map f (x:xs) = f x : map f xs

File IO

Operating on stdin/stdout is good for scripts (and this is how tools like sed or perl -p work), but for 'real' programs we'll at least need to do some file IO. The basic operations of files are:

    readFile  :: FilePath -> IO String
    writeFile :: FilePath -> String -> IO ()

'readFile' takes a file name as an argument, does some IO, and returns the file's contents as a string. 'writeFile' takes a file name, a string, and does some IO (writing that string to the file), before returning the void (or unit) value, ().


We could implement a 'cp' program on files, as:

    import System.Environment

    main = do
        [f,g] <- getArgs
        s     <- readFile f
        writeFile g s

Running this program:

   $ ghc -O A.hs
   $ ./a.out A.hs Z.hs
   $ cat Z.hs
   import System.Environment
   main = do
       [f,g] <- getArgs
       s     <- readFile f
       writeFile g s

Since we're doing IO (the type of readFile and writeFile enforce this), the code runs inside a do-block, using the IO monad. "Using the IO monad" just means that we wish to use an imperative, sequential order of evaluation. (As an aside, a wide range of other monads exist, for programming different program evaluation strategies, such as Prolog-style backtracking, or continutation-based evaluation. All of imperative programming is just one subset of possible evaluation strategies you can use in Haskell, via monads).


In do-notation, whenever you wish to run an action, for its side effect, and save the result to a variable, you would write:

    v <- action

For example, to run the 'readFile' action, which has the side effect of reading a file from disk, we say:

    s <- readFile f

Finally, we can use the 'appendFile' function to append to an existing file.


File Handles

The most generic interface to files is provided via Handles. Sometimes we need to keep a file open, for multiple reads or writes. To do this we use Handles, an abstraction much like the underlying system's file handles.


To open up a file, and get its Handle, we use:

    openFile :: FilePath -> IOMode -> IO Handle

So to open a file for reading only, in GHCi:

    Prelude System.IO> h <- openFile "A.hs" ReadMode
    {handle: A.hs}

Which returns a Handle onto the file "A.hs". We can read a line from this handle:

    Prelude System.IO> hGetLine h
    "main = do"

To close a Handle, and flush the buffer:

    hClose :: Handle -> IO ()

Once a Handle is closed, we can no longer read from it:

    Prelude System.IO> hClose h
    Prelude System.IO> hGetLine h
    *** Exception: A.hs: hGetLine: illegal operation (handle is closed)

We can also flush explicitly with:

    hFlush :: Handle -> IO ()

Other useful operations for reading from Handles:

    hGetChar     :: Handle -> IO Char
    hGetLine     :: Handle -> IO [Char]
    hGetContents :: Handle -> IO [Char]

To write to Handles:

    hPutChar    :: Handle -> Char -> IO ()
    hPutStr     :: Handle -> [Char] -> IO ()
    hPutStrLn   :: Handle -> [Char] -> IO ()
    hPrint      :: Show a => Handle -> a -> IO ()

Some other useful actions:

    hSeek     :: Handle -> SeekMode -> Integer -> IO ()
    hTell     :: Handle -> IO Integer
    hFileSize :: Handle -> IO Integer
    hIsEOF    :: Handle -> IO Bool

An example: spell checking

Here is a small example of combining the Data.Set and List data structures from yesterday's tutorial, with more IO operations. We'll implement a little spell checker, building the dictionary in a Set data type. First, some libraries to import:

    import System.Environment
    import Control.Monad
    import Data.Set

And the complete program:

    main = do
        [s] <- getArgs
        f   <- readFile "/usr/share/dict/words"
        g   <- readFile s
        let dict = fromList (lines f)
        mapM_ (spell dict) (words g)

    spell d w = when (w `notMember` d) (putStrLn w)

Running this program, on its own source, and it reports the following words are not found in the dictionary:

   $ ghc -O Spell.hs -o spell
   $ ./spell A.hs
   Data.Char
   =
   <-
   (map
   toUpper
   n)
   =
   <-
   getLine
   1


Writing the results out

If we wanted to write the results out to a temporary file, we can do so. Let's import a couple of other modules:

    import Data.Set
    import Data.Maybe
    import Text.Printf
    import System.IO
    import System.Environment
    import System.Posix.Temp

Refactoring the main code to separate out the reading and writing phases in to their own function, we end up with the core code:

    main = do
        (f, g) <- readFiles
        let dict = fromList (lines f)
            errs = mapMaybe (spell dict) (words g)
        write errs

    spell d w | w `notMember` d = Just w
              | otherwise       = Nothing

Where reading is now its own function:

    readFiles = do
        [s] <- getArgs
        f   <- readFile "/usr/share/dict/words"
        g   <- readFile s
        return (f,g)

And writing errors out to their own file:

    write errs = do
        (t,h) <- mkstemp "/tmp/spell.XXXXXX"
        mapM_ (hPutStrLn h) errs
        hClose h
        printf "%d spelling errors written to '%s'\n" (length errs) t

Pretty simple! Running this program:

   $ ghc --make -O Spell.hs -o myspell
   [1 of 1] Compiling Main             ( Spell.hs, Spell.o )
   Linking myspell ...
   $ ./myspell Spell.hs
   67 spelling errors written to '/tmp/spell.ia8256'

Extension: using SMP parallelism

Finally, just for some bonus fun ... and hold on to your hat 'cause I'm going to go fast ... we'll add some parallelism to the mix.


Haskell was designed from the start to support easy parallelisation, and since GHC 6.6, multithreaded code will run transparently on multicore systems using as many cores as you specify. Let's look at how we'd parallelise our little program to exploit multiple cores. We'll use an explicit threading model, via Control.Concurrent. You can also make your code implicitly parallel, using ">Control.Parallel.Strategies, but we'll leave that for another tutorial.


Here's the source, for you to ponder. First some imports:

    import Data.Set hiding (map)
    import Data.Maybe
    import Data.Char
    import Text.Printf
    import System.IO
    import System.Environment
    import Control.Concurrent
    import Control.Monad

The entry point, modified to break the word list into chunks, and then dispatching a chunk to each thread:

    main = do
        (f, g, n) <- readFiles
        let dict = fromList (lines f)
            work = chunk n (words g)
        run n dict work

The 'run' function sets up a channel between the main thread and all children thread ('errs'), and prints spelling errors as they arrive on the channel from the children. It then forks off 'n' children threads on each piece of the work list:

    run n dict work = do
        chan <- newChan
        errs <- getChanContents chan    -- errors returned back to main thread
        mapM_ (forkIO . thread chan dict) (zip [1..n] work)
        wait n errs 0


The main thread then just waits on all the threads to finish, printing any spelling errors they pass up:

    wait n xs i = when (i < n) $ case xs of
        Nothing : ys -> wait n ys $! i+1
        Just s  : ys -> putStrLn s >> wait n ys i

Each thread spell checks its own piece of the work list. If it finds a spelling error, it passes the offending word back over the channel to the main thread.

    thread chan dict (me, xs) = do
        mapM_ spellit xs
        writeChan chan Nothing

     where
        spellit w = when (spell dict w) $
            writeChan chan . Just $ printf "Thread %d: %-25s" (me::Int) w

The 'spell' function is simplified a little:

    spell d w = w `notMember` d

which we could also write as:

    spell = flip notMember

We modify the readFiles phase to take an additional numeric command line argument, specifying the number of threads to run:

    readFiles = do
        [s,n] <- getArgs
        f     <- readFile "/usr/share/dict/words"
        g     <- readFile s
        return (f,g, read n)

We compile this with the GHC SMP parallel runtime system:

   $ ghc -O --make -threaded Spell.hs -o spell

Now, we can run 'n' worker threads (lightweight Haskell threads), mapped onto 'm' OS threads. Since I'm using a 4 core linux server, we'll play around with 4 OS threads. First, running everything in a single thread:

   $ time ./spell test.txt 1 +RTS -N1
   ...
   Thread 1: week:
   Thread 1: IO!
   ./spell test.txt 1 +RTS -N1 99% cpu 2.533 total

Ok, now we change the command line flag to run it with 4 OS threads, to try to utilise all 4 cores:

   $ time ./spell 4 +RTS -N4
   ...
   Thread 2: week:
   Thread 3: IO!
   ./spell test.txt 4 +RTS -N4 111% cpu 2.335 total

Ok. Good... A little bit faster, uses a little bit more cpu. It turns out however the program is bound currently by the time spent in the main thread building the initial dictionary. Actual searching time is only some 10% of the running time. Nonetheless, it was fairly painless to break up the initial simple program into a parallel version.


If the program running time was extended (as the case for a server), the parallelism would be a win. Additionally, should we buy more cores for the server, all we need to is change the +RTS -N argument to the program, to start utilising these extra cores.

Next week

In upcoming tutorials we'll look more into implicitly parallel programs, and the use of the new high performance ByteString data type for string processing.