Question 1: Parametric Polymorphism and Types

1a)

Question

What is parametric polymorphism and why is it so common in Haskell?

Like generics in Java (0.5 marks) we can use type variables (0.5 marks) in Haskell to represent unconstrained, or arbitrary (0.5 marks) types for values. Effectively type variables are universally quantified (0.5 marks). It allows us to maintain static type safety (0.5 marks) while expressing computation in a generic way (0.5 marks), regardless of actual types (0.5 marks) max of 1.5 marks for the definition.

Due to Haskell’s powerful type inference system (0.5 marks), we can resolve type variables at compile time. The functional nature of Haskell (0.5 marks) and its widespread use of container data structures (0.5 marks) make polymorphism an efficient way to code generically. max of 1.5 marks for the explanation.

1b)

Abstract

Consider the Haskell functions with their type signatures listed below. For each function, give an actual function definition that would conform to the type signature shown. In each case, also give a short documentation-style explanation of the behaviour for the particular function you define.

(i) f :: a -> a

f x = x — identity function

g x y = x — selector, return first of two parameters

(iii) h :: a -> [b]

1c)

Abstract

Now consider this Haskell function:

 

twice f x = f $ f x — apply f two times to x

(i) Give the Haskell type of the function twice and explain how you have inferred this type.

 
(a->a)->a->a
 
(a->a) -> (a->a) -> a -> a
(a->a) -> a -> a
 

Question 2: Lists and Lazy Evaluation

Abstract

This question is about lists in Haskell. Note that, where you are asked to produce source code, minor syntax errors will not be penalised so long as your intention is clear and correct.

2a)

Question

Give a Haskell definition for an infinite list of strings, yn :: [String], where every even indexed element of the list is the String value “yes” and every odd indexed element of the list is the String value “no”. Try to ensure your definition of yn is concise.

2b)

Question

Briefly discuss (in 50 words or fewer) the features of the Haskell language that enable the definition of such an infinite data structure.

2c)

Question

Give a QuickCheck property specification p::Int->Bool that could be used to test that odd indexed elements of list yn are equal to the String value “no”.

2d)

Question

Will a test run of quickCheck p actually terminate? If so, why does it terminate? If not, why not?

Yes it will terminate (1 mark). Even though the list is infinite, QuickCheck only runs a finite number of test cases (1 mark).

2e)

Question

Give the Haskell definition for a function butter :: String -> String which appends the String value " but " onto its argument. For instance butter "head" will evaluate to "head but ".

2f)

Question

Now give the Haskell definition for a function uncertainty :: [String] -> String where the invocation uncertainty yn will evaluate to the String value "yes but no but yes but no but". You should use appropriate functions from the Haskell Prelude, along with the butter function from above.

2g)

Question

Observe that the infinite list yn only contains two distinct values. Is it possible for a Haskell list to contain an infinite number of distinct values? If so, give an example list. If not, explain why not.

Question 3: Email API and Monads

Abstract

This question involves a scenario about sending emails. Some of the APIs are real, whereas others are fictitious. You have encountered the real APIs throughout the course. The question provides you with all the relevant documentation required to make use of the fictitious APIs. Note that developing compilable Haskell code will simply take too long. Minor syntax errors will not be penalised. First, study the data type definition below. This is used to represent email addresses.

 

data Address = Address { user :: String, domain :: String }

3a)

Question

Write Haskell source code to turn an arbitrary Address value into an appropriate String value, by means of implementing the Show typeclass. The generated String value should concatenate the user and domain values, with an ’@’ character in between them.

 
instance Show Address of
	show (Address user domain) = user ++ "@" ++ domain
 

3b)

Question

Write Haskell source code, including an appropriate type signature, for getAddressFromUser, which prompts the user for a String value via interactive keyboard input and produces the appropriate Address value, if the input is valid. For the purposes of this question, you may assume that valid user input would be a String including a single ’@’ character, which is neither the first nor the last character. Your code should handle invalid input in a reasonable, idiomatic fashion for Haskell. Note that you may make use of the splitOn API function:

 

splitOn :: String — String to split on, error if empty String — Input text [String] — Result — break input text String into pieces — separated by the first String — argument, consuming the delimiter.

getAddressFromUser:: IO (Maybe Address)
getAddressFromUser = do
	input <- getLine
	parts = splitOn "@" input
	if length parts /= 2 then
		return Nothing
	else
		return $ Just $ Address (parts !! 0) (parts !! 1)

3c)

Question

Now consider the following API functions:

 

mkMail :: Address — sender email address (from) Address — recipient email address (to) String — email subject String — email body Email — result sendMail :: String — hostname of smtp server String — username for login String — password for login Email — message to send IO() — result


Why does the sendMail function have an IO result but the mkMail function does not have an IO result?

3d)

Question

Study the following specification carefully, then write Haskell code to meet this specification.

You may find it helpful to use higher-order list functions like map, mapM, mapM_, or similar. Your task is to produce an implementation of the function sendEmails :: [Address] -> IO (). This function prompts the user for input with a question: ‘what is your email address?’ then receives an answer using getAddressFromUser as defined above. If the input email address is not valid, the function sets the sender address to “noreply@gla.ac.uk”. Now the code should construct an email to send to each of the addresses in the list supplied as the parameter of the sendEmails function. Each of these emails should come from same sender address. Each of these emails should have a subject set to the String value “Hello” and a body set to the String value “I hope your exam is going well.”. The emails should all be sent via the server smtp.gla.ac.uk with username foo and password secret.

There are lots of possible solutions to this exercise. Marks will be awarded for correctness, as well as for idiomatic, elegant functional code.

sendEmails :: [Address] -> IO ()
sendEmails emailList = do
	putStrLn "What is your email address?"
	givenAddress <- getAddressFromUser
	let userAddress =
		case givenAddress of
			Nothing -> (Address "noreply" "gla.ac.uk")
			(Just a) -> a
	let emails = map (\x -> mkEmail givenAddress x "Hello" "I hope your exam is going well") emailList
	mapM_ (\x -> sendMail "smtp.gla.ac.uk" "foo" "secret" x) emails

— 1 for sendMail with args — 2 for point-free, lets, and/or other elegant style