Chapter 5: Types

This note captures my solutions to exercises in Chapter 5: Types of the book Haskell programming from first principles

Exercises: Type Matching

  1. The type signature for not is Bool -> Bool.
  2. The type signature for length is [a] -> Int.
  3. The type signature for concat is [[a]] -> [a].
  4. The type signature for head is [a] -> a.
  5. The type signature for (<) is Ord a => a -> a -> Bool.
-- 1. a) not
not :: Bool -> Bool -- correct answer is 2. c)
-- 1. b) length
length :: [a] -> Int -- correct answer is 2. d)
-- 1. c) concat
concat :: [[a]] -> [a] -- correct answer is 2. b)
-- 1. d) head
head :: [a] -> a -- correct answer is 2. a)
-- 1. e) (<)
(<) :: Ord a => a -> a -> Bool -- correct answer is 2. e)

Exercises: Type Arguments

  1. If the type of f is a -> a -> a -> a, and the type of x is Char, then the type of f x is Char -> Char -> Char.
  2. If the type of g is a -> b -> c -> b, then the type of g 0 'c' "woot" is Char.
  3. If the type of h is (Num a, Num b) => a -> b -> b, then the type of h 1.0 2 is Num b => b.
  4. If the type of h is (Num a, Num b) => a -> b -> b, then the type of h 1 (5.5 :: Double) is Double.
  5. If the type of jackal is (Ord a, Eq b) => a -> b -> a, then the type of jackal "keyboard" "has the word jackal in it" is [Char].
  6. If the type of jackal is (Ord a, Eq b) => a -> b -> a, then the type of jackal "keyboard" is Eq b => b -> [Char].
  7. If the type of kessel is (Ord a, Num b) => a -> b -> a, then the type of kessel 1 2 is (Num a, Ord a) => a.
  8. If the type of kessel is (Ord a, Num b) => a -> b -> a, then the type of kessel 1 (2 :: Integer) is (Num a, Ord a) => a.
  9. If the type of kessel is (Ord a, Num b) => a -> b -> a, then the type of kessel (1 :: Integer) 2 is Integer.

Exercises: Parametricity

Given the type a -> a, which is the type for id, this function cannot do anything else other than returning the same type as that of the given value.

-- #1
f :: a -> a
f a = a

The type a -> a -> a has only two implementations.

-- #2
f :: a -> a -> a
f a b = a

f' :: a -> a -> a
f' a b = b

The type a -> b -> b can only have one implementation. The behavior changes when the types of b changes, but not when the type of a changes.

-- #3
f :: a -> b -> b
f a b = b

Exercises: Apply Yourself

Question 1

(++) :: [a] -> [a] -> [a]

myConcat :: [Char] -> [Char]
myConcat x = x ++ " yo"

The fully polymorphic type signature becomes concrete types [Char] -> [Char]

Question 2

(*) :: Num a => a -> a -> a

myMult :: Fractional a => a -> a
myMult x = (x / 3) * 5

The type signature Num a => a -> a -> a becomes constrained to Fractional a => a -> a signature. The numeric typeclass becomes further contrained to a fractional typeclass.

Question 3

take :: Int -> [a] -> [a]

myTake :: Int -> [Char]
myTake x = take x "hey you"

The type variable [a] becomes a concrete type [Char] because "hey you" has a type signature of [Char].

Question 4

(>) :: Ord a => a -> a -> Bool

myCom :: Int -> Bool
myCom x = x > (length [1..10])

The type variable a becomes a concrete type Int because length [1..10] has a type signature Int.

Question 5

(<) :: Ord a => a -> a -> Bool

myAlph :: Char -> Bool
myAlph x = x < 'z'

The type variable a becomes a concrete type Char because z has a concrete type Char.

Chapter Exercises

Multiple Choice

  1. (c) A value of type [a] is a list whose elements are all of some type a.
  2. (a) A function of type [[a]] -> [a] could take a list of strings as an argument.
  3. (b) A function of type [a] -> Int -> a returns one element of type a from a list.
  4. (c) A function of type (a, b) -> a takes a tuple argument and returns the first value.

Determine the type

  1. (a) (* 9) 6 returns value :: type 54 :: Num a => a
  2. (b) head [(0,"doge"),(1,"kitteh")] returns value :: type (0,"doge") :: Num a => (a, [Char])
  3. (c) head [(0 :: Integer ,"doge"),(1,"kitteh")] returns value :: type (0,"doge") :: (Integer, [Char])
  4. (d) if False then True else False returns value :: type False :: Bool
  5. (e) length [1, 2, 3, 4, 5] returns value :: type 5 :: Int
  6. (f) (length [1, 2, 3, 4]) > (length "TACOCAT") returns value :: type False :: Bool
{-# LANGUAGE NoMonomorphismRestriction #-}

module DetermineTheType where

example = 1

x :: Num a => a
x = 5

y :: Num a => a
y = x + 5

w :: Num a => a
w = y * 10
-- The type of `w` is `w :: Num a => a`

z :: Num a => a -> a
z y = y * 10
-- The type of `z` is `z :: Num a => a -> a`

f :: Fractional a => a
f = 4 / y
-- The type of `f` is `f :: Fractional a => a`

x' :: String
x' = "Julie"

y' :: String
y' = " <3 "

z' :: String
z' = "Haskell"

f' :: [Char]
f' = x' ++ y' ++ z'
-- The type of f` is  `f :: [Char]`

determineTheType.hs

Does it compile?

-- 1. `wahoo` breaks because `bigNum` is a value, not a function.
-- bigNum = (^) 5 $ 10 
-- wahoo = bigNum $ 10

-- We can have the code below instead:
bigNum = (^ 5)
wahoo = bigNum 10

-- 2. This works. W
-- x = print
-- y = print "woohoo!" 
-- z = x "hello world"

-- We can improve as shown below:
x = print
y = x "woohoo!"
z = x "hello world"

-- 3. `c` and `d` breaks because `b` is a value, not a function. 
-- a = (+) b=5
-- c = b 10 
-- d = c 200

-- We can use the code below instead:
a = (+)
b = 5
c = a b 10
d = a c 200

-- 4. `b` breaks because `c` is not in scope. 
-- a = 12 + b
-- b = 10000 * c

-- I can fix by putting `c` in scope. 
-- Order does not matter if code is in a source file. 
-- In `GHCi`, I will need to define `c` first.

a' = 12 + b'
b' = 10000 * c'
c' = 1

doesItCompile.hs

Type Variable or Specific Type Constructor?

  1. In f :: Num a => a -> b -> Int -> Int, a is constrained polymorphic (Num), b is fully polymorphic, and Int is a concrete type constructor.
  2. In f :: zed -> Zed -> Blah, zed is fully polymorphic, Zed and Blah are concrete type constructors.
  3. In f :: Enum b => a -> b -> C, a is fully polymorphic, b is constrained polymorphic (Enum), and C is a concrete type constructor.
  4. In f :: f -> g -> C, f and g are fully polymorphic and C is a concrete type constructor.

Write a type signature

module TypeSignature where

-- Write a Type Signature
functionH :: [a] -> a
functionH (x:_) = x

functionC :: Ord a => a -> a -> Bool
functionC x y = if (x > y) then True else False

functionS :: (a, b) -> b
functionS (x, y) = y

typeSignature.hs

Given a type, write the function

module WriteTheFunction where

--  Given a Type, Write the Function

-- This is the same as the identify function `id`.
-- #1 
i :: a -> a
i x = x

-- #2
c :: a -> b -> a
c x _ = x

-- `c` and `c''` are alpha equivalent.
-- #3
c'' :: b -> a -> b
c'' x _ = x

-- #4
c' :: a -> b -> b
c' _ y = y

-- There are multiple possibilities. These include:
-- #5
-- Tail function for a list
r :: [a] -> [a]
r (_:xs) = xs
r [] = []

-- Identity function for a list
r' :: [a] -> [a]
r' x = x

-- #6
co :: (b -> c) -> (a -> b) -> a -> c
co f g x = f (g x)

-- #7
a :: (a -> c) -> a -> a
a _ x = x

-- #8
a' :: (a -> b) -> a -> b
a' f x = f x

writeTheFunction.hs

Fix it

module Sing where

fstString :: [Char] -> [Char]
fstString x = x ++ " in the rain"

sndString :: [Char] -> [Char]
sndString x = x ++ " over the rainbdow"

sing :: [Char] -> [Char] -> [Char]
sing x y = if (x > y) then fstString x else sndString y
         where x = "Singin"
               y = "Somewhere"

sing.hs

module Sing2 where

fstString :: [Char] -> [Char]
fstString x = x ++ " in the rain"

sndString :: [Char] -> [Char]
sndString x = x ++ " over the rainbdow"

sing :: [Char] -> [Char] -> [Char]
sing x y = if (x < y) then fstString x else sndString y
         where x = "Singin"
               y = "Somewhere"

sing2.hs

module Arith3Broken where

main :: IO ()
main = do
  print $ 1 + 2
  putStrLn $ show 10
  print (negate (-1))
  print ((+) 0 blah)
    where blah = negate 1

arith3broken.hs

Type-Kwon-Do

module TypeKwonDo where

-- Type-Kwon-Do

data Woot
data Blah

f :: Woot -> Blah
f = undefined

g :: (Blah, Woot) -> (Blah, Blah)
g (b, w) = (b, f w)

-- 1. This is a composition `h x = g (f x)`.
f' :: Int -> String
f' = undefined

g' :: String -> Char
g' = undefined

h' :: Int -> Char
h' = g' . f'

-- 2. This is also a composition `e a = w (q a)`.
data A
data B
data C

q :: A -> B
q = undefined

w :: B -> C
w = undefined

e :: A -> C
e = w . q

-- 3. This accepts inputs `x` and `y` and uses the function `xz` and `yz`.
data X
data Y
data Z

xz :: X -> Z
xz = undefined

yz :: Y -> Z
yz = undefined

xform :: (X, Y) -> (Z, Z)
xform (x, y) = (xz x, yz y)

-- 4. This is another composition `munge f g x = fst (g (f x))`
munge :: (x -> y)
      -> (y -> (w, z))
      -> x
      -> w
munge f g =  fst . g . f

typeKwonDo.hs