Introduction to Functional Programming – Exercises Week 4: "IO and Testing"TDA555 / DIT440, LP1 2015
Home | Schedule | Labs | Exercises | Exam | About | FAQFire | Forum | TimeEdit | Links
Introduction to Functional Programming – Exercises Week 4: "IO and Testing"TDA555 / DIT440, LP1 2015
Home | Schedule | Labs | Exercises | Exam | About | FAQFire | Forum | TimeEdit | Links

Here are some exercises designed to help you practice programming with IO and testing.

Please prepare yourself for these exercises by printing them out, and also printing out the code samples and documentation that is referred to by links.

If you do not have time to do all these exercises, don't worry. The exercises are intended to provide enough work to keep the most experienced students busy. If you do all exercises marked with an (*) you have probably understood this week's material.

Good luck!

1 (*). Properties of the Look Function

Consider the following Haskell function (known as lookup in the Prelude), that looks up an element in a list of pairs (table):
look :: Eq a => a -> [(a,b)] -> Maybe b
look x []           = Nothing
look x ((x',y):xys)
  | x == x'         = Just y
  | otherwise       = look x xys
Define a property prop_LookNothing that expresses that if the look function delivers Nothing, then the thing we were looking for was not in the table.

Define a property prop_LookJust that expresses that if the look function delivers a result (Just y), then the pair (x,y) should have been in the table.

The first component of each pair in a table is called its key. Define a property prop_LookSucceed that expresses that if the table contains the key k, then looking up the key k will succeed (i.e. look will return Just ...).

Also write one property prop_Look that combines prop_LookNothing and prop_Just into one property.

2. Properties of the prefixOf function

Consider the following Haskell function (known as isPrefixOf in the Data.List module), that checks if a list is a prefix of another list:
prefixOf :: Eq a => [a] -> [a] -> Bool
prefixOf [] _          = True
prefixOf _  []         = False
prefixOf (x:xs) (y:ys) = x == y && prefixOf xs ys

Define a property prop_prefixOfSelf that expresses that taking any number of elements (using take) from a string s gives a list which is a prefix of s.

Can you come up with a similar property for isSuffixOf (which checks if a list is a suffix of another list)?

An alternative implementation of prefixOf could be

prefixOf_alt :: Eq a => [a] -> [a] -> Bool
prefixOf_alt xs ys = take (length ys) xs == ys

Rather than actually using this definition, try to use this definition as a property. I.e. write a property of prefixOf that uses take, length and == as above.

Do you think this is a good property for testing prefixOf? Think about whether the property is mostly true or mostly false.

3. Monadic helper functions

Give an implementation of the following two functions:
sequence_ :: Monad m => [m ()] -> m ()
onlyIf    :: Monad m => Bool -> m () -> m ()
"sequence_" takes a list of instructions resulting in an uninteresting value, and creates one big instruction that executes all of these.

"onlyIf" takes a boolean and an instruction, and creates an instruction that only executes the argument instruction if the boolean was True. If the boolean was False, nothing happens. Example "onlyIf failed tryAgain" executes the instructions tryAgain only if the boolean failed is True.

Hint: You might find it easier to think of the above functions having type:

sequence_ :: [IO ()] -> IO ()
onlyIf    :: Bool -> IO () -> IO ()

What becomes different if we change the type of onlyIf to:

onlyIfM :: Monad m => m Bool -> m () -> m ()
What other kinds of programs can we write now?

Give an implementation of onlyIfM.

4 (*). The Number Guessing Game

In this exercise, you are going to implement the "number guessing game" in Haskell.

Here is an example of how this might work:

Main> game
Think of a number between 1 and 100!
Is it 50? higher
Is it 75? lower
Is it 62? lower
Is it 56? yes
Great, I won!
The text in italics is what the user types in. The other text is produced by your program.

Implement a function

game :: IO ()
That plays one instance of this game.

You might need the following functions:

getLine :: IO String         -- reads a line of user input
putStrLn :: String -> IO ()  -- outputs one line of text
Before you start programming, think of a good guessing strategy for the computer that minimizes the number of guesses!

5. A Backup Script

Haskell programs can generate instructions to be performed by the command shell using

system :: String -> IO ()

This is one way to run other programs from Haskell, for example. The String passed to system often differs, depending on whether it is the Linux shell or the Windows one which should obey the command. For example, to copy file A to file B under Linux, the string "cp A B" is used, while under Windows it would be "copy A B". As a result, programs which use system normally work only under one operating system (OS).

For doing file manipulation, it is often a better idea to use the module System.Directory, which offers functions for OS-independent file manipulation. For example

copyFile :: FilePath -> FilePath -> IO ()
can be used to copy a file on any OS.

Use the functions in System.Directory and possibly the system command to write a program that

(You are supposed to copy the files one by one, not by constructing a command such as cp * backup, because filename expansion isn't applied to strings passed to system.)

You will need to read the list of filenames in the current directory. Consult System.Directory to find a suitable function.

You will also perhaps need to perform all of a list of actions. You may find the function

sequence :: Monad m => [m a] -> m [a]
useful for this. You must add
import Control.Monad

to your file if you want to use it.