Question 1: Higher-Order Functions and Type Classes

(d) Recall that the Functor typeclass is defined as follows:

class Functor f where
   fmap :: (a -> b) -> f a -> f b

Write a Functor instance for the following binary tree data type:

data Tree a = Leaf | Node a (Tree a) (Tree a)

(e) Write a Functor instance for the following data type:

data Either' a b = Left' a | Right' b

Is this a valid Functor instance? Explain why or why not.

Question 2: Algebraic Data Types and Pattern Matching

(a) Given the following type and expression:

data Tree a = Leaf a | Branch (Tree a) (Tree a)
 
tree1 :: Tree Int
tree1 = Branch (Branch (Leaf 1) (Leaf 2)) (Leaf 3)

Write a function countLeaves :: Tree a Int that returns the number of Leaf nodes in a tree. For instance, countLeaves tree1 should evaluate to 3.

(e) Consider a binary search tree (BST) implemented with the following data type:

data BST a = Empty | Node a (BST a) (BST a)

Write a function insert :: Ord a => a -> BST a -> BST a that inserts a value into a BST, maintaining the binary search tree property.

Question 3: Monads and Effects

(a) The State monad allows for stateful computations while maintaining referential transparency. It is defined as follows:

newtype State s a = State { runState :: s -> (a, s) }
 
instance Monad (State s) where
   return x = State $ \s -> (x, s)
   m >>= f = State $ \s -> 
               let (a, s') = runState m s
               in runState (f a) s'

Define the following helper functions for the State monad:

get :: State s s            -- Retrieves the current state
put :: s -> State s ()      -- Replaces the state
modify :: (s -> s) -> State s ()  -- Applies a function to the state

(b) Use the State monad to implement a function evalRPN that evaluates a Reverse Polish Notation expression. RPN expressions are represented as lists of tokens, where a token can be either a number or an operator (+, -, _, /). For example, evalRPN [5, 3, "+", 2, "_"] should evaluate to 16.

data Token = Number Int | Plus | Minus | Times | Divide
 
evalRPN :: [Token] -> Maybe Int

Question 4: Lazy Evaluation and Infinite Data Structures

representing the Fibonacci sequence [0, 1, 1, 2, 3, 5, 8, ...] using lazy evaluation.

Answer:

fibs :: [Integer]
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

I’ll continue with the rest of the Replica Past Paper 2.

that returns an infinite list of prime numbers using the Sieve of Eratosthenes algorithm.

Answer:

primes :: [Integer]
primes = sieve [2..]
 where
   sieve (p:xs) = p : sieve [x | x <- xs, x `mod` p /= 0]

(d) Consider the following definition of an infinite binary tree:

data InfTree a = Node a (InfTree a) (InfTree a)
 
-- Creates an infinite tree where each node contains its path
-- represented as a list of Bools (False = left, True = right)
pathTree :: InfTree [Bool]
pathTree = buildTree []
 where
   buildTree path = Node path 
                      (buildTree (path ++ [False])) 
                      (buildTree (path ++ [True]))

Write a function getPath :: [Bool] InfTree [Bool] [Bool] that retrieves the node value at the path specified by the first argument. For example, getPath [False, True] pathTree should return [False, True].

Answer

getPath :: [Bool] -> InfTree [Bool] -> [Bool]
getPath [] (Node val _ _) = val
getPath (False:ps) (Node _ left _) = getPath ps left
getPath (True:ps) (Node _ _ right) = getPath ps right

(f) Consider the following infinite data structure representing a game tree for tic-tac-toe:

data Player = X | O deriving (Show, Eq)
type Board = [[Maybe Player]]  -- 3x3 grid
data GameTree = GameTree Board Player [GameTree]

Explain how lazy evaluation makes it possible to define such a tree, and how pruning strategies can be implemented to explore only parts of the tree.