Working with the IO monad in Haskell.
Beginning of class session 19.
HaskellHomework
within your local copy of your SVN repository. Do an SVN update on that folder and you’ll find a file WackyMaps.hs
. Except where indicated, all your work for this assignment must be done in that file. This is so we can efficiently grade your and your classmates’ work.
The file includes the line import Data.Map (Map, empty, insert, findWithDefault)
. This imports a library for manipulating finite maps (also known as dictionaries) in Haskell. Experiment with the following functions until you understand what they do:
empty :: Map k a
insert :: (Ord k) => k -> a -> Map k a -> Map k a
findWithDefault :: (Ord k) => a -> k -> Map k a -> a
Can you predict the values of v1
, v2
, and v3
after loading the following code? (You don’t have to turn in any work for this task, but you should check your work using ghci
.)
m1 = empty m2 = insert 'x' 10 m1 m3 = insert 'y' 20 m2 v1 = findWithDefault 0 'x' m3 v2 = findWithDefault 0 'y' m3 v3 = findWithDefault 0 'z' m3 m4 = insert 'y' 30 m3 v4 = findWithDefault 0 'y' m4
Implement a Wacky Prof. Quotes program as described in homework 3. Some suggestions:
String
to represent professors’ names, dates, and quotes. Create type synonyms for these—Prof
, Date
, and Quote
—so your code is more readable.
data DateQuotePair = DQPair Date Quote deriving (Show,Read)
repl :: (Map String [DateQuotePair]) -> IO ()
repl
as being like the accumulator in a tail-recursive implementation of a function.
You might want to create a main
IO action to run your loop. This worked for me to start:
main :: IO () main = repl empty
After I implemented file I/O, I changed this to:
main :: IO () main = do db <- getDatabaseFromDisk repl db
getDatabaseFromDisk
: wpqFileName = "wpq.txt" getDatabaseFromDisk :: IO (Map Prof [DateQuotePair]) getDatabaseFromDisk = catch (readAttempt) (\exc -> return empty) where readAttempt = do contents <- readFile wpqFileName let db = ((read contents) :: (Map Prof [DateQuotePair])) {- '$!' below forces strict processing of the db argument, rather than non-strict (lazy) processing. This causes the entire file contents to be loaded into memory, but prevents the problem where Haskell gives a permission error on writing to the file because it has finished lazily reading from the file. -} return $! db
I used a couple of helper IO actions named addQuote
and printQuotes
, with the same type as repl
. I used another helper IO action with the type:
saveMap :: (Map Prof [DateQuotePair]) -> IO ()
Hint: To implement this last helper, note that the Map
datatype implements the Show
typeclass. That is, you can use a single show
call to convert a Map
into a String
that could later be read by the getDatabaseFromDisk
function.