date sorting II

Claus Reinke
Sun, 10 Mar 2002 16:19:22 -0000

> So Here's the code... It is my first Haskell program so please forgive me if 
> I broke any rules. I have been trying to debug it but keep running into 
> walls, any and all help will be appreciated.

This is probably not quite what you expected, but I'd like to try a little
experiment: instead of completely rewriting your code by thinking about
what it should do, I've made it runnable through a series of small changes,
trying to stick closely to the code you've given. And instead of giving you 
the resulting code, I'll just give you my change log, so that you can try to 
reproduce the steps.

If this is absolutely no help, I can send my version of your code, but as
this kind of problem comes up again and again, I'd first like to see whether 
this style of approach can be of more help than just posting a solution.

---------change log follows

1. - eliminated globals myList, myDates, myfile
   - they don't seem to serve any purpose at the moment, and
     while your imperative programming style suggests that you
     want to use them, they shouldn't be necessary
2. - trying to load into Hugs - type error in use of while (in
     getInput): loop body returns something other than ().
   - you definitely want to keep the lines, so we have to change
     the definition of while, not its use (looking at getInput
     , which uses IO in the definition, but doesn't mention IO in 
     its type, we'll have to change getInput as well, but first
     things first)
3. - according to the way getInput is used in start, I interpret
     your comments as saying getInput should return a list of 
     Strings, one for each line of input.
   - getInput is defined in terms of while, so we need to make
     while return a list of Strings as well.
   - the action you pass to while is essentially hGetLine, so it
     seems easiest to adapt while to deal with an action that
     returns a String (instead of an action that returns
     while :: IO Bool -> IO a -> IO [a]
4. - this means we have to modify the use of while in getInput
     slightly: the body of the loop now only has to return a
     single line, and while takes care of collecting the lines
     into a list of Strings
   - however, all that still involves IO, so the type should be:
     getInput :: Handle -> IO [String]
5. - next stop (it helps to reload the partially correct program
     into Hugs every now and then, as a kind of minimum test
     suite, a program should load without problems): stripSpaces
     is a plain function 
     stripSpaces :: [String] -> [[String]] 
     As such, it cannot be used directly as an IO action.
   - either turn stripSpaces into an IO action, or (prefered) use
     it as the function it is to compute the parameter to qsort
     without IO (one way to do that: replace the monadic binding
     "myDates <- ..;" by a let binding "let {myDates = ..};"
6. - now we've got the same problem with qsort
     qSort :: [[String]] -> [[String]]
     It's a function, not an IO action
   - same remedy as before: use let to bind the sorted myDates
7. - next in line: formatOutput
     formatOutput:: [[String]] -> [String]
     no IO in sight, none needed.
   - you've heard this before:) use let to bind the result of
8. - major breakthrough: the program loads for the first time!
   - we've not said anything about correctness yet, but you might
     want to clean up the code a bit before proceeding:
     - the let bindings in start can be merged, if you rename the
       variables a bit - e.g., call the result of qsort
       sortedDates instead of myDates (you could also inline the 
       definitions, but names for intermediate values are a useful 
       documentation aid)
     - in getInput, no do is needed for the body of the loop -
       just (hGetLine myfile) should do
     - it is always worth browsing the Haskell Prelude and
       Standard Libraries for things that do what you need: e.g., right
       next to hGetLine in module IO there is hGetContents, which
       combines nicely with lines from module PreludeList
     - its is good practice to close files you've opened after
       use; and as that is so easy to forget, module IO provides
       the bracket and bracket_ combinators (openFile and hClose
       are similar to opening and closing brackets around the
       part of the program that uses the filehandle)
     - does getOutput really give you the output you want?

Having thus made your program runnable, you can now experiment
(in fact, usually one would build up such programs by developing
runnable, working fragments and composing them into bigger
fragments). Having simplified the code, you can then think about
correctness and efficiency.